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]); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||