mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
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
This commit is contained in:
parent
02d7bb8604
commit
1c63ac6a3a
5 changed files with 204 additions and 0 deletions
6
resources/sql/autopatches/20160616.repo.01.oldref.sql
Normal file
6
resources/sql/autopatches/20160616.repo.01.oldref.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_repository.repository_oldref (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
repositoryPHID VARBINARY(64) NOT NULL,
|
||||||
|
commitIdentifier VARCHAR(40) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
KEY `key_repository` (repositoryPHID)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -3277,6 +3277,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
|
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
|
||||||
'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
|
'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
|
||||||
'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
|
'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
|
||||||
|
'PhabricatorRepositoryOldRef' => 'applications/repository/storage/PhabricatorRepositoryOldRef.php',
|
||||||
'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
|
'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
|
||||||
'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
|
'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
|
||||||
'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
|
'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
|
||||||
|
@ -8067,6 +8068,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
|
||||||
'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
|
'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
|
||||||
|
'PhabricatorRepositoryOldRef' => array(
|
||||||
|
'PhabricatorRepositoryDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
'PhabricatorRepositoryParsedChange' => 'Phobject',
|
'PhabricatorRepositoryParsedChange' => 'Phobject',
|
||||||
'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
|
'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
|
||||||
'PhabricatorRepositoryPullEvent' => array(
|
'PhabricatorRepositoryPullEvent' => array(
|
||||||
|
|
|
@ -105,6 +105,8 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
$this->commitCache[$ref->getIdentifier()] = true;
|
$this->commitCache[$ref->getIdentifier()] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->markUnreachableCommits($repository);
|
||||||
|
|
||||||
$version = $this->getObservedVersion($repository);
|
$version = $this->getObservedVersion($repository);
|
||||||
if ($version !== null) {
|
if ($version !== null) {
|
||||||
id(new DiffusionRepositoryClusterEngine())
|
id(new DiffusionRepositoryClusterEngine())
|
||||||
|
@ -731,4 +733,136 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
return (int)$version['version'];
|
return (int)$version['version'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function markUnreachableCommits(PhabricatorRepository $repository) {
|
||||||
|
// For now, this is only supported for Git.
|
||||||
|
if (!$repository->isGit()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find older versions of refs which we haven't processed yet. We're going
|
||||||
|
// to make sure their commits are still reachable.
|
||||||
|
$old_refs = id(new PhabricatorRepositoryOldRef())->loadAllWhere(
|
||||||
|
'repositoryPHID = %s',
|
||||||
|
$repository->getPHID());
|
||||||
|
|
||||||
|
// We can share a single graph stream across all the checks we need to do.
|
||||||
|
$stream = new PhabricatorGitGraphStream($repository);
|
||||||
|
|
||||||
|
foreach ($old_refs as $old_ref) {
|
||||||
|
$identifier = $old_ref->getCommitIdentifier();
|
||||||
|
$this->markUnreachableFrom($repository, $stream, $identifier);
|
||||||
|
|
||||||
|
// If nothing threw an exception, we're all done with this ref.
|
||||||
|
$old_ref->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markUnreachableFrom(
|
||||||
|
PhabricatorRepository $repository,
|
||||||
|
PhabricatorGitGraphStream $stream,
|
||||||
|
$identifier) {
|
||||||
|
|
||||||
|
$unreachable = array();
|
||||||
|
|
||||||
|
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
|
||||||
|
'repositoryID = %s AND commitIdentifier = %s',
|
||||||
|
$repository->getID(),
|
||||||
|
$identifier);
|
||||||
|
if (!$commit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$look = array($commit);
|
||||||
|
$seen = array();
|
||||||
|
while ($look) {
|
||||||
|
$target = array_pop($look);
|
||||||
|
|
||||||
|
// If we've already checked this commit (for example, because history
|
||||||
|
// branches and then merges) we don't need to check it again.
|
||||||
|
$target_identifier = $target->getCommitIdentifier();
|
||||||
|
if (isset($seen[$target_identifier])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[$target_identifier] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stream->getCommitDate($target_identifier);
|
||||||
|
$reachable = true;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$reachable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reachable) {
|
||||||
|
// This commit is reachable, so we don't need to go any further
|
||||||
|
// down this road.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unreachable[] = $target;
|
||||||
|
|
||||||
|
// Find the commit's parents and check them for reachability, too. We
|
||||||
|
// have to look in the database since we no may longer have the commit
|
||||||
|
// in the repository.
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$commit->establishConnection('w'),
|
||||||
|
'SELECT commit.* FROM %T commit
|
||||||
|
JOIN %T parents ON commit.id = parents.parentCommitID
|
||||||
|
WHERE parents.childCommitID = %d',
|
||||||
|
$commit->getTableName(),
|
||||||
|
PhabricatorRepository::TABLE_PARENTS,
|
||||||
|
$target->getID());
|
||||||
|
if (!$rows) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parents = id(new PhabricatorRepositoryCommit())
|
||||||
|
->loadAllFromArray($rows);
|
||||||
|
foreach ($parents as $parent) {
|
||||||
|
$look[] = $parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$unreachable = array_reverse($unreachable);
|
||||||
|
|
||||||
|
$flag = PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE;
|
||||||
|
foreach ($unreachable as $unreachable_commit) {
|
||||||
|
$unreachable_commit->writeImportStatusFlag($flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If anything was unreachable, just rebuild the whole summary table.
|
||||||
|
// We can't really update it incrementally when a commit becomes
|
||||||
|
// unreachable.
|
||||||
|
if ($unreachable) {
|
||||||
|
$this->rebuildSummaryTable($repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rebuildSummaryTable(PhabricatorRepository $repository) {
|
||||||
|
$conn_w = $repository->establishConnection('w');
|
||||||
|
|
||||||
|
$data = queryfx_one(
|
||||||
|
$conn_w,
|
||||||
|
'SELECT COUNT(*) N, MAX(id) id, MAX(epoch) epoch
|
||||||
|
FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d',
|
||||||
|
id(new PhabricatorRepositoryCommit())->getTableName(),
|
||||||
|
$repository->getID(),
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
||||||
|
VALUES (%d, %d, %d, %d)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
size = VALUES(size),
|
||||||
|
lastCommitID = VALUES(lastCommitID),
|
||||||
|
epoch = VALUES(epoch)',
|
||||||
|
PhabricatorRepository::TABLE_SUMMARY,
|
||||||
|
$repository->getID(),
|
||||||
|
$data['N'],
|
||||||
|
$data['id'],
|
||||||
|
$data['epoch']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,13 @@ final class PhabricatorRepositoryRefEngine
|
||||||
$ref->save();
|
$ref->save();
|
||||||
}
|
}
|
||||||
foreach ($this->deadRefs as $ref) {
|
foreach ($this->deadRefs as $ref) {
|
||||||
|
// Shove this ref into the old refs table so the discovery engine
|
||||||
|
// can check if any commits have been rendered unreachable.
|
||||||
|
id(new PhabricatorRepositoryOldRef())
|
||||||
|
->setRepositoryPHID($repository->getPHID())
|
||||||
|
->setCommitIdentifier($ref->getCommitIdentifier())
|
||||||
|
->save();
|
||||||
|
|
||||||
$ref->delete();
|
$ref->delete();
|
||||||
}
|
}
|
||||||
$repository->saveTransaction();
|
$repository->saveTransaction();
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores outdated refs which need to be checked for reachability.
|
||||||
|
*
|
||||||
|
* When a branch is deleted, the old HEAD ends up here and the discovery
|
||||||
|
* engine marks all the commits that previously appeared on it as unreachable.
|
||||||
|
*/
|
||||||
|
final class PhabricatorRepositoryOldRef
|
||||||
|
extends PhabricatorRepositoryDAO
|
||||||
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
protected $repositoryPHID;
|
||||||
|
protected $commitIdentifier;
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'commitIdentifier' => 'text40',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_repository' => array(
|
||||||
|
'columns' => array('repositoryPHID'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return PhabricatorPolicies::getMostOpenPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue