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')); $status_value = idx($value_map, idx($dict, 'status'));
$ansi_color = idx($color_map, $status_value); $ansi_color = idx($color_map, $status_value);
$date_created = null;
if (isset($dict['dateCreated'])) {
$date_created = (int)$dict['dateCreated'];
}
$dict['fields'] = array( $dict['fields'] = array(
'uri' => idx($dict, 'uri'), 'uri' => idx($dict, 'uri'),
'title' => idx($dict, 'title'), 'title' => idx($dict, 'title'),
@ -80,6 +85,7 @@ final class ArcanistRevisionRef
'value' => $status_value, 'value' => $status_value,
'color.ansi' => $ansi_color, 'color.ansi' => $ansi_color,
), ),
'dateCreated' => $date_created,
); );
return self::newFromConduit($dict); return self::newFromConduit($dict);
@ -104,6 +110,10 @@ final class ArcanistRevisionRef
return idxv($this->parameters, array('fields', 'status', 'color.ansi')); return idxv($this->parameters, array('fields', 'status', 'color.ansi'));
} }
public function getDateCreated() {
return idxv($this->parameters, array('fields', 'dateCreated'));
}
public function isStatusChangesPlanned() { public function isStatusChangesPlanned() {
$status = $this->getStatus(); $status = $this->getStatus();
return ($status === 'changes-planned'); return ($status === 'changes-planned');

View file

@ -7,8 +7,9 @@ abstract class ArcanistCommitGraphQuery
private $headHashes; private $headHashes;
private $tailHashes; private $tailHashes;
private $exactHashes; private $exactHashes;
private $stopAtGCA;
private $limit; private $limit;
private $minimumEpoch;
private $maximumEpoch;
final public function setGraph(ArcanistCommitGraph $graph) { final public function setGraph(ArcanistCommitGraph $graph) {
$this->graph = $graph; $this->graph = $graph;
@ -46,11 +47,6 @@ abstract class ArcanistCommitGraphQuery
return $this->exactHashes; return $this->exactHashes;
} }
final public function withStopAtGCA($stop_gca) {
$this->stopAtGCA = $stop_gca;
return $this;
}
final public function setLimit($limit) { final public function setLimit($limit) {
$this->limit = $limit; $this->limit = $limit;
return $this; return $this;
@ -60,6 +56,20 @@ abstract class ArcanistCommitGraphQuery
return $this->limit; 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() { final public function getRepositoryAPI() {
return $this->getGraph()->getRepositoryAPI(); return $this->getGraph()->getRepositoryAPI();
} }

View file

@ -3,17 +3,21 @@
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();
@ -22,72 +26,122 @@ final class ArcanistGitCommitGraphQuery
} }
$api = $this->getRepositoryAPI(); $api = $this->getRepositoryAPI();
$ref_lists = array();
$refs = array(); if ($head_hashes) {
if ($head_hashes !== null) { $refs = array();
foreach ($head_hashes as $hash) { if ($head_hashes !== null) {
$refs[] = $hash; foreach ($head_hashes as $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);
}
} }
$ref_lists[] = $refs;
} }
if ($exact_hashes !== null) { 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) { 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;
} }
} }
$refs[] = '--'; $flags = array();
$refs = implode("\n", $refs)."\n";
$fields = array( $min_epoch = $this->getMinimumEpoch();
'%e', if ($min_epoch !== null) {
'%H', $flags[] = '--after';
'%P', $flags[] = date('c', $min_epoch);
'%ct', }
'%B',
);
$format = implode('%x02', $fields).'%x01'; $max_epoch = $this->getMaximumEpoch();
if ($max_epoch !== null) {
$flags[] = '--before';
$flags[] = date('c', $max_epoch);
}
$future = $api->newFuture( foreach ($ref_lists as $ref_list) {
'log --format=%s --stdin', $ref_blob = implode("\n", $ref_list)."\n";
$format);
$future->write($refs);
$future->setResolveOnError(true);
$future->start();
$lines = id(new LinesOfALargeExecFuture($future)) $fields = array(
->setDelimiter("\1"); '%e',
$lines->rewind(); '%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(); $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();
@ -133,6 +187,7 @@ final class ArcanistGitCommitGraphQuery
$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 {
@ -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) { if ($revsets) {
$revsets = array( $revsets = array(
$this->joinAndRevsets($revs), $this->joinAndRevsets($revsets),
); );
} }
@ -58,12 +58,10 @@ final class ArcanistMercurialCommitGraphQuery
'%s', '%s',
$exact_hash); $exact_hash);
} }
$revsets[] = array( $revsets[] = $this->joinOrRevsets($revs);
$this->joinOrRevsets($revs),
);
} }
$revsets = $this->joinOrRevsets($revs); $revsets = $this->joinOrRevsets($revsets);
$fields = array( $fields = array(
'', // Placeholder for "encoding". '', // Placeholder for "encoding".
@ -75,10 +73,42 @@ final class ArcanistMercurialCommitGraphQuery
$template = implode("\2", $fields)."\1"; $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( $future = $api->newFuture(
'log --rev %s --template %s --', 'log --rev %s --template %s %Ls --',
$revsets, $revsets,
$template); $template,
$flags);
$future->setResolveOnError(true); $future->setResolveOnError(true);
$future->start(); $future->start();

View file

@ -60,17 +60,11 @@ abstract class ArcanistMarkersWorkflow
} }
if ($disjoint_heads) { 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 $nodes += $disjoint_nodes;
// time.
foreach ($disjoint_heads as $disjoint_head) {
$disjoint_nodes = $graph->newQuery()
->withExactHashes(array($disjoint_head))
->execute();
$nodes += $disjoint_nodes;
}
} }
$state_refs = array(); $state_refs = array();