Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2889285
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
29 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/repository/daemon/PhabricatorGitGraphStream.php b/src/applications/repository/daemon/PhabricatorGitGraphStream.php
index 669cf9c105..b175053528 100644
--- a/src/applications/repository/daemon/PhabricatorGitGraphStream.php
+++ b/src/applications/repository/daemon/PhabricatorGitGraphStream.php
@@ -1,95 +1,105 @@
<?php
final class PhabricatorGitGraphStream
extends PhabricatorRepositoryGraphStream {
private $repository;
private $iterator;
+ private $startCommit;
private $parents = array();
private $dates = array();
public function __construct(
PhabricatorRepository $repository,
$start_commit = null) {
$this->repository = $repository;
+ $this->startCommit = $start_commit;
if ($start_commit !== null) {
$future = $repository->getLocalCommandFuture(
'log --format=%s %s --',
'%H%x01%P%x01%ct',
$start_commit);
} else {
$future = $repository->getLocalCommandFuture(
'log --format=%s --all --',
'%H%x01%P%x01%ct');
}
$this->iterator = new LinesOfALargeExecFuture($future);
$this->iterator->setDelimiter("\n");
$this->iterator->rewind();
}
public function getParents($commit) {
if (!isset($this->parents[$commit])) {
$this->parseUntil($commit);
}
$parents = $this->parents[$commit];
// NOTE: In Git, it is possible for a commit to list the same parent more
// than once. See T5226. Discard duplicate parents.
return array_unique($parents);
}
public function getCommitDate($commit) {
if (!isset($this->dates[$commit])) {
$this->parseUntil($commit);
}
return $this->dates[$commit];
}
private function parseUntil($commit) {
if ($this->isParsed($commit)) {
return;
}
$gitlog = $this->iterator;
while ($gitlog->valid()) {
$line = $gitlog->current();
$gitlog->next();
$line = trim($line);
if (!strlen($line)) {
break;
}
list($hash, $parents, $epoch) = explode("\1", $line);
if ($parents) {
$parents = explode(' ', $parents);
} else {
// First commit.
$parents = array();
}
$this->dates[$hash] = $epoch;
$this->parents[$hash] = $parents;
if ($this->isParsed($commit)) {
return;
}
}
- throw new Exception(
- pht(
- "No such commit '%s' in repository!",
- $commit));
+ if ($this->startCommit !== null) {
+ throw new Exception(
+ pht(
+ 'Commit "%s" is not a reachable ancestor of "%s".',
+ $commit,
+ $this->startCommit));
+ } else {
+ throw new Exception(
+ pht(
+ 'Commit "%s" is not a reachable ancestor of any ref.',
+ $commit));
+ }
}
private function isParsed($commit) {
return isset($this->dates[$commit]);
}
}
diff --git a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
index cdb7e11d03..fd8c57e9b4 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
@@ -1,868 +1,882 @@
<?php
/**
* @task discover Discovering Repositories
* @task svn Discovering Subversion Repositories
* @task git Discovering Git Repositories
* @task hg Discovering Mercurial Repositories
* @task internal Internals
*/
final class PhabricatorRepositoryDiscoveryEngine
extends PhabricatorRepositoryEngine {
private $repairMode;
private $commitCache = array();
private $workingSet = array();
const MAX_COMMIT_CACHE_SIZE = 2048;
/* -( Discovering Repositories )------------------------------------------- */
public function setRepairMode($repair_mode) {
$this->repairMode = $repair_mode;
return $this;
}
public function getRepairMode() {
return $this->repairMode;
}
/**
* @task discovery
*/
public function discoverCommits() {
$repository = $this->getRepository();
$lock = $this->newRepositoryLock($repository, 'repo.look', false);
try {
$lock->lock();
} catch (PhutilLockException $ex) {
throw new DiffusionDaemonLockException(
pht(
'Another process is currently discovering repository "%s", '.
'skipping discovery.',
$repository->getDisplayName()));
}
try {
$result = $this->discoverCommitsWithLock();
} catch (Exception $ex) {
$lock->unlock();
throw $ex;
}
$lock->unlock();
return $result;
}
private function discoverCommitsWithLock() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$refs = $this->discoverSubversionCommits();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$refs = $this->discoverMercurialCommits();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$refs = $this->discoverGitCommits();
break;
default:
throw new Exception(pht("Unknown VCS '%s'!", $vcs));
}
if ($this->isInitialImport($refs)) {
$this->log(
pht(
'Discovered more than %s commit(s) in an empty repository, '.
'marking repository as importing.',
new PhutilNumber(PhabricatorRepository::IMPORT_THRESHOLD)));
$repository->markImporting();
}
// Clear the working set cache.
$this->workingSet = array();
// Record discovered commits and mark them in the cache.
foreach ($refs as $ref) {
$this->recordCommit(
$repository,
$ref->getIdentifier(),
$ref->getEpoch(),
$ref->getCanCloseImmediately(),
$ref->getParents());
$this->commitCache[$ref->getIdentifier()] = true;
}
$this->markUnreachableCommits($repository);
$version = $this->getObservedVersion($repository);
if ($version !== null) {
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyAfterDiscovery($version);
}
return $refs;
}
/* -( Discovering Git Repositories )--------------------------------------- */
/**
* @task git
*/
private function discoverGitCommits() {
$repository = $this->getRepository();
if (!$repository->isHosted()) {
$this->verifyGitOrigin($repository);
}
$heads = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->execute();
if (!$heads) {
// This repository has no heads at all, so we don't need to do
// anything. Generally, this means the repository is empty.
return array();
}
$heads = $this->sortRefs($heads);
$head_commits = mpull($heads, 'getCommitIdentifier');
$this->log(
pht(
'Discovering commits in repository "%s".',
$repository->getDisplayName()));
$this->fillCommitCache($head_commits);
$refs = array();
foreach ($heads as $ref) {
$name = $ref->getShortName();
$commit = $ref->getCommitIdentifier();
- $this->log(pht('Examining ref "%s", at "%s".', $name, $commit));
+ $this->log(
+ pht(
+ 'Examining "%s" (%s) at "%s".',
+ $name,
+ $ref->getRefType(),
+ $commit));
if (!$repository->shouldTrackRef($ref)) {
$this->log(pht('Skipping, ref is untracked.'));
continue;
}
if ($this->isKnownCommit($commit)) {
$this->log(pht('Skipping, HEAD is known.'));
continue;
}
+ // In Git, it's possible to tag a tag. We just skip these, we'll discover
+ // them when we process the target tag. See T11180.
+ $fields = $ref->getRawFields();
+ $tag_type = idx($fields, '*objecttype');
+ if ($tag_type == 'tag') {
+ $this->log(pht('Skipping, this is a tag of a tag.'));
+ continue;
+ }
+
$this->log(pht('Looking for new commits.'));
$head_refs = $this->discoverStreamAncestry(
new PhabricatorGitGraphStream($repository, $commit),
$commit,
$repository->shouldAutocloseRef($ref));
$this->didDiscoverRefs($head_refs);
$refs[] = $head_refs;
}
return array_mergev($refs);
}
/* -( Discovering Subversion Repositories )-------------------------------- */
/**
* @task svn
*/
private function discoverSubversionCommits() {
$repository = $this->getRepository();
if (!$repository->isHosted()) {
$this->verifySubversionRoot($repository);
}
$upper_bound = null;
$limit = 1;
$refs = array();
do {
// Find all the unknown commits on this path. Note that we permit
// importing an SVN subdirectory rather than the entire repository, so
// commits may be nonsequential.
if ($upper_bound === null) {
$at_rev = 'HEAD';
} else {
$at_rev = ($upper_bound - 1);
}
try {
list($xml, $stderr) = $repository->execxRemoteCommand(
'log --xml --quiet --limit %d %s',
$limit,
$repository->getSubversionBaseURI($at_rev));
} catch (CommandException $ex) {
$stderr = $ex->getStdErr();
if (preg_match('/(path|File) not found/', $stderr)) {
// We've gone all the way back through history and this path was not
// affected by earlier commits.
break;
}
throw $ex;
}
$xml = phutil_utf8ize($xml);
$log = new SimpleXMLElement($xml);
foreach ($log->logentry as $entry) {
$identifier = (int)$entry['revision'];
$epoch = (int)strtotime((string)$entry->date[0]);
$refs[$identifier] = id(new PhabricatorRepositoryCommitRef())
->setIdentifier($identifier)
->setEpoch($epoch)
->setCanCloseImmediately(true);
if ($upper_bound === null) {
$upper_bound = $identifier;
} else {
$upper_bound = min($upper_bound, $identifier);
}
}
// Discover 2, 4, 8, ... 256 logs at a time. This allows us to initially
// import large repositories fairly quickly, while pulling only as much
// data as we need in the common case (when we've already imported the
// repository and are just grabbing one commit at a time).
$limit = min($limit * 2, 256);
} while ($upper_bound > 1 && !$this->isKnownCommit($upper_bound));
krsort($refs);
while ($refs && $this->isKnownCommit(last($refs)->getIdentifier())) {
array_pop($refs);
}
$refs = array_reverse($refs);
$this->didDiscoverRefs($refs);
return $refs;
}
private function verifySubversionRoot(PhabricatorRepository $repository) {
list($xml) = $repository->execxRemoteCommand(
'info --xml %s',
$repository->getSubversionPathURI());
$xml = phutil_utf8ize($xml);
$xml = new SimpleXMLElement($xml);
$remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
$expect_root = $repository->getSubversionPathURI();
$normal_type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
$remote_normal = id(new PhabricatorRepositoryURINormalizer(
$normal_type_svn,
$remote_root))->getNormalizedPath();
$expect_normal = id(new PhabricatorRepositoryURINormalizer(
$normal_type_svn,
$expect_root))->getNormalizedPath();
if ($remote_normal != $expect_normal) {
throw new Exception(
pht(
'Repository "%s" does not have a correctly configured remote URI. '.
'The remote URI for a Subversion repository MUST point at the '.
'repository root. The root for this repository is "%s", but the '.
'configured URI is "%s". To resolve this error, set the remote URI '.
'to point at the repository root. If you want to import only part '.
'of a Subversion repository, use the "Import Only" option.',
$repository->getDisplayName(),
$remote_root,
$expect_root));
}
}
/* -( Discovering Mercurial Repositories )--------------------------------- */
/**
* @task hg
*/
private function discoverMercurialCommits() {
$repository = $this->getRepository();
$branches = id(new DiffusionLowLevelMercurialBranchesQuery())
->setRepository($repository)
->execute();
$this->fillCommitCache(mpull($branches, 'getCommitIdentifier'));
$refs = array();
foreach ($branches as $branch) {
// NOTE: Mercurial branches may have multiple heads, so the names may
// not be unique.
$name = $branch->getShortName();
$commit = $branch->getCommitIdentifier();
$this->log(pht('Examining branch "%s" head "%s".', $name, $commit));
if (!$repository->shouldTrackBranch($name)) {
$this->log(pht('Skipping, branch is untracked.'));
continue;
}
if ($this->isKnownCommit($commit)) {
$this->log(pht('Skipping, this head is a known commit.'));
continue;
}
$this->log(pht('Looking for new commits.'));
$branch_refs = $this->discoverStreamAncestry(
new PhabricatorMercurialGraphStream($repository, $commit),
$commit,
$close_immediately = true);
$this->didDiscoverRefs($branch_refs);
$refs[] = $branch_refs;
}
return array_mergev($refs);
}
/* -( Internals )---------------------------------------------------------- */
private function discoverStreamAncestry(
PhabricatorRepositoryGraphStream $stream,
$commit,
$close_immediately) {
$discover = array($commit);
$graph = array();
$seen = array();
// Find all the reachable, undiscovered commits. Build a graph of the
// edges.
while ($discover) {
$target = array_pop($discover);
if (empty($graph[$target])) {
$graph[$target] = array();
}
$parents = $stream->getParents($target);
foreach ($parents as $parent) {
if ($this->isKnownCommit($parent)) {
continue;
}
$graph[$target][$parent] = true;
if (empty($seen[$parent])) {
$seen[$parent] = true;
$discover[] = $parent;
}
}
}
// Now, sort them topographically.
$commits = $this->reduceGraph($graph);
$refs = array();
foreach ($commits as $commit) {
$refs[] = id(new PhabricatorRepositoryCommitRef())
->setIdentifier($commit)
->setEpoch($stream->getCommitDate($commit))
->setCanCloseImmediately($close_immediately)
->setParents($stream->getParents($commit));
}
return $refs;
}
private function reduceGraph(array $edges) {
foreach ($edges as $commit => $parents) {
$edges[$commit] = array_keys($parents);
}
$graph = new PhutilDirectedScalarGraph();
$graph->addNodes($edges);
$commits = $graph->getTopographicallySortedNodes();
// NOTE: We want the most ancestral nodes first, so we need to reverse the
// list we get out of AbstractDirectedGraph.
$commits = array_reverse($commits);
return $commits;
}
private function isKnownCommit($identifier) {
if (isset($this->commitCache[$identifier])) {
return true;
}
if (isset($this->workingSet[$identifier])) {
return true;
}
if ($this->repairMode) {
// In repair mode, rediscover the entire repository, ignoring the
// database state. We can hit the local cache above, but if we miss it
// stop the script from going to the database cache.
return false;
}
$this->fillCommitCache(array($identifier));
return isset($this->commitCache[$identifier]);
}
private function fillCommitCache(array $identifiers) {
if (!$identifiers) {
return;
}
// When filling the cache we ignore commits which have been marked as
// unreachable, treating them as though they do not exist. When recording
// commits later we'll revive commits that exist but are unreachable.
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'repositoryID = %d AND commitIdentifier IN (%Ls)
AND (importStatus & %d) != %d',
$this->getRepository()->getID(),
$identifiers,
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
foreach ($commits as $commit) {
$this->commitCache[$commit->getCommitIdentifier()] = true;
}
while (count($this->commitCache) > self::MAX_COMMIT_CACHE_SIZE) {
array_shift($this->commitCache);
}
}
/**
* Sort branches so we process closeable branches first. This makes the
* whole import process a little cheaper, since we can close these commits
* the first time through rather than catching them in the refs step.
*
* @task internal
*
* @param list<DiffusionRepositoryRef> List of refs.
* @return list<DiffusionRepositoryRef> Sorted list of refs.
*/
private function sortRefs(array $refs) {
$repository = $this->getRepository();
$head_refs = array();
$tail_refs = array();
foreach ($refs as $ref) {
if ($repository->shouldAutocloseRef($ref)) {
$head_refs[] = $ref;
} else {
$tail_refs[] = $ref;
}
}
return array_merge($head_refs, $tail_refs);
}
private function recordCommit(
PhabricatorRepository $repository,
$commit_identifier,
$epoch,
$close_immediately,
array $parents) {
$commit = new PhabricatorRepositoryCommit();
$conn_w = $repository->establishConnection('w');
// First, try to revive an existing unreachable commit (if one exists) by
// removing the "unreachable" flag. If we succeed, we don't need to do
// anything else: we already discovered this commit some time ago.
queryfx(
$conn_w,
'UPDATE %T SET importStatus = (importStatus & ~%d)
WHERE repositoryID = %d AND commitIdentifier = %s',
$commit->getTableName(),
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
$repository->getID(),
$commit_identifier);
if ($conn_w->getAffectedRows()) {
$commit = $commit->loadOneWhere(
'repositoryID = %d AND commitIdentifier = %s',
$repository->getID(),
$commit_identifier);
// After reviving a commit, schedule new daemons for it.
$this->didDiscoverCommit($repository, $commit, $epoch);
return;
}
$commit->setRepositoryID($repository->getID());
$commit->setCommitIdentifier($commit_identifier);
$commit->setEpoch($epoch);
if ($close_immediately) {
$commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE);
}
$data = new PhabricatorRepositoryCommitData();
try {
// If this commit has parents, look up their IDs. The parent commits
// should always exist already.
$parent_ids = array();
if ($parents) {
$parent_rows = queryfx_all(
$conn_w,
'SELECT id, commitIdentifier FROM %T
WHERE commitIdentifier IN (%Ls) AND repositoryID = %d',
$commit->getTableName(),
$parents,
$repository->getID());
$parent_map = ipull($parent_rows, 'id', 'commitIdentifier');
foreach ($parents as $parent) {
if (empty($parent_map[$parent])) {
throw new Exception(
pht('Unable to identify parent "%s"!', $parent));
}
$parent_ids[] = $parent_map[$parent];
}
} else {
// Write an explicit 0 so we can distinguish between "really no
// parents" and "data not available".
if (!$repository->isSVN()) {
$parent_ids = array(0);
}
}
$commit->openTransaction();
$commit->save();
$data->setCommitID($commit->getID());
$data->save();
foreach ($parent_ids as $parent_id) {
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (childCommitID, parentCommitID)
VALUES (%d, %d)',
PhabricatorRepository::TABLE_PARENTS,
$commit->getID(),
$parent_id);
}
$commit->saveTransaction();
$this->didDiscoverCommit($repository, $commit, $epoch);
if ($this->repairMode) {
// Normally, the query should throw a duplicate key exception. If we
// reach this in repair mode, we've actually performed a repair.
$this->log(pht('Repaired commit "%s".', $commit_identifier));
}
PhutilEventEngine::dispatchEvent(
new PhabricatorEvent(
PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT,
array(
'repository' => $repository,
'commit' => $commit,
)));
} catch (AphrontDuplicateKeyQueryException $ex) {
$commit->killTransaction();
// Ignore. This can happen because we discover the same new commit
// more than once when looking at history, or because of races or
// data inconsistency or cosmic radiation; in any case, we're still
// in a good state if we ignore the failure.
}
}
private function didDiscoverCommit(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit,
$epoch) {
$this->insertTask($repository, $commit);
// Update the repository summary table.
queryfx(
$commit->establishConnection('w'),
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
VALUES (%d, 1, %d, %d)
ON DUPLICATE KEY UPDATE
size = size + 1,
lastCommitID =
IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
PhabricatorRepository::TABLE_SUMMARY,
$repository->getID(),
$commit->getID(),
$epoch);
}
private function didDiscoverRefs(array $refs) {
foreach ($refs as $ref) {
$this->workingSet[$ref->getIdentifier()] = true;
}
}
private function insertTask(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit,
$data = array()) {
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$class = 'PhabricatorRepositoryGitCommitMessageParserWorker';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$class = 'PhabricatorRepositorySvnCommitMessageParserWorker';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$class = 'PhabricatorRepositoryMercurialCommitMessageParserWorker';
break;
default:
throw new Exception(pht("Unknown repository type '%s'!", $vcs));
}
$data['commitID'] = $commit->getID();
PhabricatorWorker::scheduleTask($class, $data);
}
private function isInitialImport(array $refs) {
$commit_count = count($refs);
if ($commit_count <= PhabricatorRepository::IMPORT_THRESHOLD) {
// If we fetched a small number of commits, assume it's an initial
// commit or a stack of a few initial commits.
return false;
}
$viewer = $this->getViewer();
$repository = $this->getRepository();
$any_commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->setLimit(1)
->execute();
if ($any_commits) {
// If the repository already has commits, this isn't an import.
return false;
}
return true;
}
private function getObservedVersion(PhabricatorRepository $repository) {
if ($repository->isHosted()) {
return null;
}
if ($repository->isGit()) {
return $this->getGitObservedVersion($repository);
}
return null;
}
private function getGitObservedVersion(PhabricatorRepository $repository) {
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->execute();
if (!$refs) {
return null;
}
// In Git, the observed version is the most recently discovered commit
// at any repository HEAD. It's possible for this to regress temporarily
// if a branch is pushed and then deleted. This is acceptable because it
// doesn't do anything meaningfully bad and will fix itself on the next
// push.
$ref_identifiers = mpull($refs, 'getCommitIdentifier');
$ref_identifiers = array_fuse($ref_identifiers);
$version = queryfx_one(
$repository->establishConnection('w'),
'SELECT MAX(id) version FROM %T WHERE repositoryID = %d
AND commitIdentifier IN (%Ls)',
id(new PhabricatorRepositoryCommit())->getTableName(),
$repository->getID(),
$ref_identifiers);
if (!$version) {
return null;
}
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']);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 11:56 (4 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124302
Default Alt Text
(29 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment