Changeset View
Changeset View
Standalone View
Standalone View
src/repository/graph/query/ArcanistGitCommitGraphQuery.php
- This file was added.
<?php | |||||
final class ArcanistGitCommitGraphQuery | |||||
extends ArcanistCommitGraphQuery { | |||||
private $queryFuture; | |||||
private $seen = array(); | |||||
public function execute() { | |||||
$this->beginExecute(); | |||||
$this->continueExecute(); | |||||
return $this->seen; | |||||
} | |||||
protected function beginExecute() { | |||||
$head_hashes = $this->getHeadHashes(); | |||||
$exact_hashes = $this->getExactHashes(); | |||||
if (!$head_hashes && !$exact_hashes) { | |||||
throw new Exception(pht('Need head hashes or exact hashes!')); | |||||
} | |||||
$api = $this->getRepositoryAPI(); | |||||
$refs = array(); | |||||
if ($head_hashes !== null) { | |||||
foreach ($head_hashes as $hash) { | |||||
$refs[] = $hash; | |||||
} | |||||
} | |||||
$tail_hashes = $this->getTailHashes(); | |||||
if ($tail_hashes !== null) { | |||||
foreach ($tail_hashes as $tail_hash) { | |||||
$refs[] = sprintf('^%s^@', $tail_hash); | |||||
} | |||||
} | |||||
if ($exact_hashes !== null) { | |||||
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.')); | |||||
} | |||||
foreach ($exact_hashes as $exact_hash) { | |||||
$refs[] = $exact_hash; | |||||
$refs[] = sprintf('^%s^@', $exact_hash); | |||||
} | |||||
} | |||||
$refs[] = '--'; | |||||
$refs = implode("\n", $refs)."\n"; | |||||
$fields = array( | |||||
'%e', | |||||
'%H', | |||||
'%P', | |||||
'%ct', | |||||
'%B', | |||||
); | |||||
$format = implode('%x02', $fields).'%x01'; | |||||
$future = $api->newFuture( | |||||
'log --format=%s --stdin', | |||||
$format); | |||||
$future->write($refs); | |||||
$future->setResolveOnError(true); | |||||
$future->start(); | |||||
$lines = id(new LinesOfALargeExecFuture($future)) | |||||
->setDelimiter("\1"); | |||||
$lines->rewind(); | |||||
$this->queryFuture = $lines; | |||||
} | |||||
protected function continueExecute() { | |||||
$graph = $this->getGraph(); | |||||
$limit = $this->getLimit(); | |||||
$lines = $this->queryFuture; | |||||
while (true) { | |||||
if (!$lines->valid()) { | |||||
return false; | |||||
} | |||||
$line = $lines->current(); | |||||
$lines->next(); | |||||
if ($line === "\n") { | |||||
continue; | |||||
} | |||||
$fields = explode("\2", $line); | |||||
if (count($fields) !== 5) { | |||||
throw new Exception( | |||||
pht( | |||||
'Failed to split line "%s" from "git log".', | |||||
$line)); | |||||
} | |||||
list($encoding, $hash, $parents, $commit_epoch, $message) = $fields; | |||||
// TODO: Handle encoding, see DiffusionLowLevelCommitQuery. | |||||
$node = $graph->getNode($hash); | |||||
if (!$node) { | |||||
$node = $graph->newNode($hash); | |||||
} | |||||
$this->seen[$hash] = $node; | |||||
$node | |||||
->setCommitMessage($message) | |||||
->setCommitEpoch((int)$commit_epoch); | |||||
if (strlen($parents)) { | |||||
$parents = explode(' ', $parents); | |||||
$parent_nodes = array(); | |||||
foreach ($parents as $parent) { | |||||
$parent_node = $graph->getNode($parent); | |||||
if (!$parent_node) { | |||||
$parent_node = $graph->newNode($parent); | |||||
} | |||||
$parent_nodes[$parent] = $parent_node; | |||||
$parent_node->addChildNode($node); | |||||
} | |||||
$node->setParentNodes($parent_nodes); | |||||
} else { | |||||
$parents = array(); | |||||
} | |||||
if ($limit) { | |||||
if (count($this->seen) >= $limit) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |