Changeset View
Changeset View
Standalone View
Standalone View
src/repository/graph/query/ArcanistGitCommitGraphQuery.php
<?php | <?php | ||||
final class ArcanistGitCommitGraphQuery | final class ArcanistGitCommitGraphQuery | ||||
extends ArcanistCommitGraphQuery { | extends ArcanistCommitGraphQuery { | ||||
private $queryFuture; | |||||
private $seen = array(); | private $seen = array(); | ||||
private $futures = array(); | |||||
private $iterators = array(); | |||||
private $cursors = array(); | |||||
private $iteratorKey = 0; | |||||
public function execute() { | public function execute() { | ||||
$this->beginExecute(); | $this->newFutures(); | ||||
$this->continueExecute(); | |||||
$this->executeIterators(); | |||||
return $this->seen; | return $this->seen; | ||||
} | } | ||||
protected function beginExecute() { | private function newFutures() { | ||||
$head_hashes = $this->getHeadHashes(); | $head_hashes = $this->getHeadHashes(); | ||||
$exact_hashes = $this->getExactHashes(); | $exact_hashes = $this->getExactHashes(); | ||||
if (!$head_hashes && !$exact_hashes) { | if (!$head_hashes && !$exact_hashes) { | ||||
throw new Exception(pht('Need head hashes or exact hashes!')); | throw new Exception(pht('Need head hashes or exact hashes!')); | ||||
} | } | ||||
$api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
$ref_lists = array(); | |||||
if ($head_hashes) { | |||||
$refs = array(); | $refs = array(); | ||||
if ($head_hashes !== null) { | if ($head_hashes !== null) { | ||||
foreach ($head_hashes as $hash) { | foreach ($head_hashes as $hash) { | ||||
$refs[] = $hash; | $refs[] = $hash; | ||||
} | } | ||||
} | } | ||||
$tail_hashes = $this->getTailHashes(); | $tail_hashes = $this->getTailHashes(); | ||||
if ($tail_hashes !== null) { | if ($tail_hashes !== null) { | ||||
foreach ($tail_hashes as $tail_hash) { | foreach ($tail_hashes as $tail_hash) { | ||||
$refs[] = sprintf('^%s^@', $tail_hash); | $refs[] = sprintf('^%s^@', $tail_hash); | ||||
} | } | ||||
} | } | ||||
if ($exact_hashes !== null) { | $ref_lists[] = $refs; | ||||
if (count($exact_hashes) > 1) { | |||||
// If "A" is a parent of "B" and we search for exact hashes ["A", "B"], | |||||
// the exclusion rule generated by "^B^@" is stronger than the inclusion | |||||
// rule generated by "A" and we don't get "A" in the result set. | |||||
throw new Exception( | |||||
pht( | |||||
'TODO: Multiple exact hashes not supported under Git.')); | |||||
} | } | ||||
if ($exact_hashes !== null) { | |||||
foreach ($exact_hashes as $exact_hash) { | foreach ($exact_hashes as $exact_hash) { | ||||
$refs[] = $exact_hash; | $ref_list = array(); | ||||
$refs[] = sprintf('^%s^@', $exact_hash); | $ref_list[] = $exact_hash; | ||||
$ref_list[] = sprintf('^%s^@', $exact_hash); | |||||
$ref_list[] = '--'; | |||||
$ref_lists[] = $ref_list; | |||||
} | |||||
} | |||||
$flags = array(); | |||||
$min_epoch = $this->getMinimumEpoch(); | |||||
if ($min_epoch !== null) { | |||||
$flags[] = '--after'; | |||||
$flags[] = date('c', $min_epoch); | |||||
} | } | ||||
$max_epoch = $this->getMaximumEpoch(); | |||||
if ($max_epoch !== null) { | |||||
$flags[] = '--before'; | |||||
$flags[] = date('c', $max_epoch); | |||||
} | } | ||||
$refs[] = '--'; | foreach ($ref_lists as $ref_list) { | ||||
$refs = implode("\n", $refs)."\n"; | $ref_blob = implode("\n", $ref_list)."\n"; | ||||
$fields = array( | $fields = array( | ||||
'%e', | '%e', | ||||
'%H', | '%H', | ||||
'%P', | '%P', | ||||
'%ct', | '%ct', | ||||
'%B', | '%B', | ||||
); | ); | ||||
$format = implode('%x02', $fields).'%x01'; | $format = implode('%x02', $fields).'%x01'; | ||||
$future = $api->newFuture( | $future = $api->newFuture( | ||||
'log --format=%s --stdin', | 'log --format=%s %Ls --stdin', | ||||
$format); | $format, | ||||
$future->write($refs); | $flags); | ||||
$future->write($ref_blob); | |||||
$future->setResolveOnError(true); | $future->setResolveOnError(true); | ||||
$future->start(); | |||||
$lines = id(new LinesOfALargeExecFuture($future)) | $this->futures[] = $future; | ||||
} | |||||
} | |||||
private function executeIterators() { | |||||
while ($this->futures || $this->iterators) { | |||||
$iterator_limit = 8; | |||||
while (count($this->iterators) < $iterator_limit) { | |||||
if (!$this->futures) { | |||||
break; | |||||
} | |||||
$future = array_pop($this->futures); | |||||
$future->startFuture(); | |||||
$iterator = id(new LinesOfALargeExecFuture($future)) | |||||
->setDelimiter("\1"); | ->setDelimiter("\1"); | ||||
$lines->rewind(); | $iterator->rewind(); | ||||
$iterator_key = $this->getNextIteratorKey(); | |||||
$this->iterators[$iterator_key] = $iterator; | |||||
} | |||||
$limit = $this->getLimit(); | |||||
foreach ($this->iterators as $iterator_key => $iterator) { | |||||
$this->executeIterator($iterator_key, $iterator); | |||||
if ($limit) { | |||||
if (count($this->seen) >= $limit) { | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
$this->queryFuture = $lines; | private function getNextIteratorKey() { | ||||
return $this->iteratorKey++; | |||||
} | } | ||||
protected function continueExecute() { | private function executeIterator($iterator_key, $lines) { | ||||
$graph = $this->getGraph(); | $graph = $this->getGraph(); | ||||
$limit = $this->getLimit(); | $limit = $this->getLimit(); | ||||
$lines = $this->queryFuture; | $is_done = false; | ||||
while (true) { | while (true) { | ||||
if (!$lines->valid()) { | if (!$lines->valid()) { | ||||
return false; | $is_done = true; | ||||
break; | |||||
} | } | ||||
$line = $lines->current(); | $line = $lines->current(); | ||||
$lines->next(); | $lines->next(); | ||||
if ($line === "\n") { | if ($line === "\n") { | ||||
continue; | continue; | ||||
} | } | ||||
Show All 29 Lines | while (true) { | ||||
foreach ($parents as $parent) { | foreach ($parents as $parent) { | ||||
$parent_node = $graph->getNode($parent); | $parent_node = $graph->getNode($parent); | ||||
if (!$parent_node) { | if (!$parent_node) { | ||||
$parent_node = $graph->newNode($parent); | $parent_node = $graph->newNode($parent); | ||||
} | } | ||||
$parent_nodes[$parent] = $parent_node; | $parent_nodes[$parent] = $parent_node; | ||||
$parent_node->addChildNode($node); | $parent_node->addChildNode($node); | ||||
} | } | ||||
$node->setParentNodes($parent_nodes); | $node->setParentNodes($parent_nodes); | ||||
} else { | } else { | ||||
$parents = array(); | $parents = array(); | ||||
} | } | ||||
if ($limit) { | if ($limit) { | ||||
if (count($this->seen) >= $limit) { | if (count($this->seen) >= $limit) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if ($is_done) { | |||||
unset($this->iterators[$iterator_key]); | |||||
} | |||||
} | } | ||||
} | } |