1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-10 00:42:40 +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:
epriestley 2020-06-30 09:15:26 -07:00
parent c7093a2e57
commit 8c95dc0d29
5 changed files with 172 additions and 69 deletions

View file

@ -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');

View file

@ -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();
}

View file

@ -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,7 +26,9 @@ final class ArcanistGitCommitGraphQuery
}
$api = $this->getRepositoryAPI();
$ref_lists = array();
if ($head_hashes) {
$refs = array();
if ($head_hashes !== null) {
foreach ($head_hashes as $hash) {
@ -37,23 +43,35 @@ final class ArcanistGitCommitGraphQuery
}
}
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.'));
$ref_lists[] = $refs;
}
if ($exact_hashes !== null) {
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();
$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);
}
foreach ($ref_lists as $ref_list) {
$ref_blob = implode("\n", $ref_list)."\n";
$fields = array(
'%e',
@ -66,28 +84,64 @@ final class ArcanistGitCommitGraphQuery
$format = implode('%x02', $fields).'%x01';
$future = $api->newFuture(
'log --format=%s --stdin',
$format);
$future->write($refs);
'log --format=%s %Ls --stdin',
$format,
$flags);
$future->write($ref_blob);
$future->setResolveOnError(true);
$future->start();
$lines = id(new LinesOfALargeExecFuture($future))
->setDelimiter("\1");
$lines->rewind();
$this->queryFuture = $lines;
$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]);
}
}
}

View file

@ -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();

View file

@ -60,18 +60,12 @@ abstract class ArcanistMarkersWorkflow
}
if ($disjoint_heads) {
// 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))
->withExactHashes($disjoint_heads)
->execute();
$nodes += $disjoint_nodes;
}
}
$state_refs = array();
foreach ($nodes as $node) {