mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-12-23 05:50:54 +01:00
Support date-range commit graph queries, and multiple disjoint commits in Git
Summary: Ref T13546. Allow the commit graph to be queried by date range, and Git to be queried for multiple disjoint commits. Test Plan: Ran "arc branches" and future code which searches for alternate commit ranges for revisions. Maniphest Tasks: T13546 Differential Revision: https://secure.phabricator.com/D21379
This commit is contained in:
parent
c7093a2e57
commit
8c95dc0d29
5 changed files with 172 additions and 69 deletions
|
@ -70,6 +70,11 @@ final class ArcanistRevisionRef
|
|||
$status_value = idx($value_map, idx($dict, 'status'));
|
||||
$ansi_color = idx($color_map, $status_value);
|
||||
|
||||
$date_created = null;
|
||||
if (isset($dict['dateCreated'])) {
|
||||
$date_created = (int)$dict['dateCreated'];
|
||||
}
|
||||
|
||||
$dict['fields'] = array(
|
||||
'uri' => idx($dict, 'uri'),
|
||||
'title' => idx($dict, 'title'),
|
||||
|
@ -80,6 +85,7 @@ final class ArcanistRevisionRef
|
|||
'value' => $status_value,
|
||||
'color.ansi' => $ansi_color,
|
||||
),
|
||||
'dateCreated' => $date_created,
|
||||
);
|
||||
|
||||
return self::newFromConduit($dict);
|
||||
|
@ -104,6 +110,10 @@ final class ArcanistRevisionRef
|
|||
return idxv($this->parameters, array('fields', 'status', 'color.ansi'));
|
||||
}
|
||||
|
||||
public function getDateCreated() {
|
||||
return idxv($this->parameters, array('fields', 'dateCreated'));
|
||||
}
|
||||
|
||||
public function isStatusChangesPlanned() {
|
||||
$status = $this->getStatus();
|
||||
return ($status === 'changes-planned');
|
||||
|
|
|
@ -7,8 +7,9 @@ abstract class ArcanistCommitGraphQuery
|
|||
private $headHashes;
|
||||
private $tailHashes;
|
||||
private $exactHashes;
|
||||
private $stopAtGCA;
|
||||
private $limit;
|
||||
private $minimumEpoch;
|
||||
private $maximumEpoch;
|
||||
|
||||
final public function setGraph(ArcanistCommitGraph $graph) {
|
||||
$this->graph = $graph;
|
||||
|
@ -46,11 +47,6 @@ abstract class ArcanistCommitGraphQuery
|
|||
return $this->exactHashes;
|
||||
}
|
||||
|
||||
final public function withStopAtGCA($stop_gca) {
|
||||
$this->stopAtGCA = $stop_gca;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
|
@ -60,6 +56,20 @@ abstract class ArcanistCommitGraphQuery
|
|||
return $this->limit;
|
||||
}
|
||||
|
||||
final public function withEpochRange($min, $max) {
|
||||
$this->minimumEpoch = $min;
|
||||
$this->maximumEpoch = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getMinimumEpoch() {
|
||||
return $this->minimumEpoch;
|
||||
}
|
||||
|
||||
final public function getMaximumEpoch() {
|
||||
return $this->maximumEpoch;
|
||||
}
|
||||
|
||||
final public function getRepositoryAPI() {
|
||||
return $this->getGraph()->getRepositoryAPI();
|
||||
}
|
||||
|
|
|
@ -3,17 +3,21 @@
|
|||
final class ArcanistGitCommitGraphQuery
|
||||
extends ArcanistCommitGraphQuery {
|
||||
|
||||
private $queryFuture;
|
||||
private $seen = array();
|
||||
private $futures = array();
|
||||
private $iterators = array();
|
||||
private $cursors = array();
|
||||
private $iteratorKey = 0;
|
||||
|
||||
public function execute() {
|
||||
$this->beginExecute();
|
||||
$this->continueExecute();
|
||||
$this->newFutures();
|
||||
|
||||
$this->executeIterators();
|
||||
|
||||
return $this->seen;
|
||||
}
|
||||
|
||||
protected function beginExecute() {
|
||||
private function newFutures() {
|
||||
$head_hashes = $this->getHeadHashes();
|
||||
$exact_hashes = $this->getExactHashes();
|
||||
|
||||
|
@ -22,72 +26,122 @@ final class ArcanistGitCommitGraphQuery
|
|||
}
|
||||
|
||||
$api = $this->getRepositoryAPI();
|
||||
$ref_lists = array();
|
||||
|
||||
$refs = array();
|
||||
if ($head_hashes !== null) {
|
||||
foreach ($head_hashes as $hash) {
|
||||
$refs[] = $hash;
|
||||
if ($head_hashes) {
|
||||
$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);
|
||||
$tail_hashes = $this->getTailHashes();
|
||||
if ($tail_hashes !== null) {
|
||||
foreach ($tail_hashes as $tail_hash) {
|
||||
$refs[] = sprintf('^%s^@', $tail_hash);
|
||||
}
|
||||
}
|
||||
|
||||
$ref_lists[] = $refs;
|
||||
}
|
||||
|
||||
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);
|
||||
$ref_list = array();
|
||||
$ref_list[] = $exact_hash;
|
||||
$ref_list[] = sprintf('^%s^@', $exact_hash);
|
||||
$ref_list[] = '--';
|
||||
$ref_lists[] = $ref_list;
|
||||
}
|
||||
}
|
||||
|
||||
$refs[] = '--';
|
||||
$refs = implode("\n", $refs)."\n";
|
||||
$flags = array();
|
||||
|
||||
$fields = array(
|
||||
'%e',
|
||||
'%H',
|
||||
'%P',
|
||||
'%ct',
|
||||
'%B',
|
||||
);
|
||||
$min_epoch = $this->getMinimumEpoch();
|
||||
if ($min_epoch !== null) {
|
||||
$flags[] = '--after';
|
||||
$flags[] = date('c', $min_epoch);
|
||||
}
|
||||
|
||||
$format = implode('%x02', $fields).'%x01';
|
||||
$max_epoch = $this->getMaximumEpoch();
|
||||
if ($max_epoch !== null) {
|
||||
$flags[] = '--before';
|
||||
$flags[] = date('c', $max_epoch);
|
||||
}
|
||||
|
||||
$future = $api->newFuture(
|
||||
'log --format=%s --stdin',
|
||||
$format);
|
||||
$future->write($refs);
|
||||
$future->setResolveOnError(true);
|
||||
$future->start();
|
||||
foreach ($ref_lists as $ref_list) {
|
||||
$ref_blob = implode("\n", $ref_list)."\n";
|
||||
|
||||
$lines = id(new LinesOfALargeExecFuture($future))
|
||||
->setDelimiter("\1");
|
||||
$lines->rewind();
|
||||
$fields = array(
|
||||
'%e',
|
||||
'%H',
|
||||
'%P',
|
||||
'%ct',
|
||||
'%B',
|
||||
);
|
||||
|
||||
$this->queryFuture = $lines;
|
||||
$format = implode('%x02', $fields).'%x01';
|
||||
|
||||
$future = $api->newFuture(
|
||||
'log --format=%s %Ls --stdin',
|
||||
$format,
|
||||
$flags);
|
||||
$future->write($ref_blob);
|
||||
$future->setResolveOnError(true);
|
||||
|
||||
$this->futures[] = $future;
|
||||
}
|
||||
}
|
||||
|
||||
protected function continueExecute() {
|
||||
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");
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getNextIteratorKey() {
|
||||
return $this->iteratorKey++;
|
||||
}
|
||||
|
||||
private function executeIterator($iterator_key, $lines) {
|
||||
$graph = $this->getGraph();
|
||||
$limit = $this->getLimit();
|
||||
|
||||
$lines = $this->queryFuture;
|
||||
$is_done = false;
|
||||
|
||||
while (true) {
|
||||
if (!$lines->valid()) {
|
||||
return false;
|
||||
$is_done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$line = $lines->current();
|
||||
|
@ -133,6 +187,7 @@ final class ArcanistGitCommitGraphQuery
|
|||
|
||||
$parent_nodes[$parent] = $parent_node;
|
||||
$parent_node->addChildNode($node);
|
||||
|
||||
}
|
||||
$node->setParentNodes($parent_nodes);
|
||||
} else {
|
||||
|
@ -145,6 +200,10 @@ final class ArcanistGitCommitGraphQuery
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_done) {
|
||||
unset($this->iterators[$iterator_key]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ final class ArcanistMercurialCommitGraphQuery
|
|||
|
||||
if ($revsets) {
|
||||
$revsets = array(
|
||||
$this->joinAndRevsets($revs),
|
||||
$this->joinAndRevsets($revsets),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -58,12 +58,10 @@ final class ArcanistMercurialCommitGraphQuery
|
|||
'%s',
|
||||
$exact_hash);
|
||||
}
|
||||
$revsets[] = array(
|
||||
$this->joinOrRevsets($revs),
|
||||
);
|
||||
$revsets[] = $this->joinOrRevsets($revs);
|
||||
}
|
||||
|
||||
$revsets = $this->joinOrRevsets($revs);
|
||||
$revsets = $this->joinOrRevsets($revsets);
|
||||
|
||||
$fields = array(
|
||||
'', // Placeholder for "encoding".
|
||||
|
@ -75,10 +73,42 @@ final class ArcanistMercurialCommitGraphQuery
|
|||
|
||||
$template = implode("\2", $fields)."\1";
|
||||
|
||||
$flags = array();
|
||||
|
||||
$min_epoch = $this->getMinimumEpoch();
|
||||
$max_epoch = $this->getMaximumEpoch();
|
||||
if ($min_epoch !== null || $max_epoch !== null) {
|
||||
$flags[] = '--date';
|
||||
|
||||
if ($min_epoch !== null) {
|
||||
$min_epoch = date('c', $min_epoch);
|
||||
}
|
||||
|
||||
if ($max_epoch !== null) {
|
||||
$max_epoch = date('c', $max_epoch);
|
||||
}
|
||||
|
||||
if ($min_epoch !== null && $max_epoch !== null) {
|
||||
$flags[] = sprintf(
|
||||
'%s to %s',
|
||||
$min_epoch,
|
||||
$max_epoch);
|
||||
} else if ($min_epoch) {
|
||||
$flags[] = sprintf(
|
||||
'>%s',
|
||||
$min_epoch);
|
||||
} else {
|
||||
$flags[] = sprintf(
|
||||
'<%s',
|
||||
$max_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
$future = $api->newFuture(
|
||||
'log --rev %s --template %s --',
|
||||
'log --rev %s --template %s %Ls --',
|
||||
$revsets,
|
||||
$template);
|
||||
$template,
|
||||
$flags);
|
||||
$future->setResolveOnError(true);
|
||||
$future->start();
|
||||
|
||||
|
|
|
@ -60,17 +60,11 @@ abstract class ArcanistMarkersWorkflow
|
|||
}
|
||||
|
||||
if ($disjoint_heads) {
|
||||
$disjoint_nodes = $graph->newQuery()
|
||||
->withExactHashes($disjoint_heads)
|
||||
->execute();
|
||||
|
||||
// TODO: Git currently can not query for more than one exact hash at a
|
||||
// time.
|
||||
|
||||
foreach ($disjoint_heads as $disjoint_head) {
|
||||
$disjoint_nodes = $graph->newQuery()
|
||||
->withExactHashes(array($disjoint_head))
|
||||
->execute();
|
||||
|
||||
$nodes += $disjoint_nodes;
|
||||
}
|
||||
$nodes += $disjoint_nodes;
|
||||
}
|
||||
|
||||
$state_refs = array();
|
||||
|
|
Loading…
Reference in a new issue