diff --git a/src/ref/revision/ArcanistRevisionRef.php b/src/ref/revision/ArcanistRevisionRef.php --- a/src/ref/revision/ArcanistRevisionRef.php +++ b/src/ref/revision/ArcanistRevisionRef.php @@ -70,6 +70,11 @@ $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 @@ 'value' => $status_value, 'color.ansi' => $ansi_color, ), + 'dateCreated' => $date_created, ); return self::newFromConduit($dict); @@ -104,6 +110,10 @@ 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'); diff --git a/src/repository/graph/query/ArcanistCommitGraphQuery.php b/src/repository/graph/query/ArcanistCommitGraphQuery.php --- a/src/repository/graph/query/ArcanistCommitGraphQuery.php +++ b/src/repository/graph/query/ArcanistCommitGraphQuery.php @@ -7,8 +7,9 @@ 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 @@ 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 @@ 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(); } diff --git a/src/repository/graph/query/ArcanistGitCommitGraphQuery.php b/src/repository/graph/query/ArcanistGitCommitGraphQuery.php --- a/src/repository/graph/query/ArcanistGitCommitGraphQuery.php +++ b/src/repository/graph/query/ArcanistGitCommitGraphQuery.php @@ -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 @@ } $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(); + + $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', + '%H', + '%P', + '%ct', + '%B', + ); + + $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; + } + } + + private function executeIterators() { + while ($this->futures || $this->iterators) { + $iterator_limit = 8; - $fields = array( - '%e', - '%H', - '%P', - '%ct', - '%B', - ); + while (count($this->iterators) < $iterator_limit) { + if (!$this->futures) { + break; + } - $format = implode('%x02', $fields).'%x01'; + $future = array_pop($this->futures); + $future->startFuture(); - $future = $api->newFuture( - 'log --format=%s --stdin', - $format); - $future->write($refs); - $future->setResolveOnError(true); - $future->start(); + $iterator = id(new LinesOfALargeExecFuture($future)) + ->setDelimiter("\1"); + $iterator->rewind(); - $lines = id(new LinesOfALargeExecFuture($future)) - ->setDelimiter("\1"); - $lines->rewind(); + $iterator_key = $this->getNextIteratorKey(); + $this->iterators[$iterator_key] = $iterator; + } + + $limit = $this->getLimit(); - $this->queryFuture = $lines; + 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++; } - protected function continueExecute() { + 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 @@ $parent_nodes[$parent] = $parent_node; $parent_node->addChildNode($node); + } $node->setParentNodes($parent_nodes); } else { @@ -145,6 +200,10 @@ } } } + + if ($is_done) { + unset($this->iterators[$iterator_key]); + } } } diff --git a/src/repository/graph/query/ArcanistMercurialCommitGraphQuery.php b/src/repository/graph/query/ArcanistMercurialCommitGraphQuery.php --- a/src/repository/graph/query/ArcanistMercurialCommitGraphQuery.php +++ b/src/repository/graph/query/ArcanistMercurialCommitGraphQuery.php @@ -47,7 +47,7 @@ if ($revsets) { $revsets = array( - $this->joinAndRevsets($revs), + $this->joinAndRevsets($revsets), ); } @@ -58,12 +58,10 @@ '%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 @@ $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(); diff --git a/src/workflow/ArcanistMarkersWorkflow.php b/src/workflow/ArcanistMarkersWorkflow.php --- a/src/workflow/ArcanistMarkersWorkflow.php +++ b/src/workflow/ArcanistMarkersWorkflow.php @@ -60,17 +60,11 @@ } 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();