private function rebuildRepository(PhabricatorRepository $repo)
 {
     $console = PhutilConsole::getConsole();
     $console->writeOut("%s\n", pht('Rebuilding "%s"...', $repo->getMonogram()));
     $refs = id(new PhabricatorRepositoryRefCursorQuery())->setViewer($this->getViewer())->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))->withRepositoryPHIDs(array($repo->getPHID()))->execute();
     $graph = array();
     foreach ($refs as $ref) {
         if (!$repo->shouldTrackBranch($ref->getRefName())) {
             continue;
         }
         $console->writeOut("%s\n", pht('Rebuilding branch "%s"...', $ref->getRefName()));
         $commit = $ref->getCommitIdentifier();
         if ($repo->isGit()) {
             $stream = new PhabricatorGitGraphStream($repo, $commit);
         } else {
             $stream = new PhabricatorMercurialGraphStream($repo, $commit);
         }
         $discover = array($commit);
         while ($discover) {
             $target = array_pop($discover);
             if (isset($graph[$target])) {
                 continue;
             }
             $graph[$target] = $stream->getParents($target);
             foreach ($graph[$target] as $parent) {
                 $discover[] = $parent;
             }
         }
     }
     $console->writeOut("%s\n", pht('Found %s total commit(s); updating...', phutil_count($graph)));
     $commit_table = id(new PhabricatorRepositoryCommit());
     $commit_table_name = $commit_table->getTableName();
     $conn_w = $commit_table->establishConnection('w');
     $bar = id(new PhutilConsoleProgressBar())->setTotal(count($graph));
     $need = array();
     foreach ($graph as $child => $parents) {
         foreach ($parents as $parent) {
             $need[$parent] = $parent;
         }
         $need[$child] = $child;
     }
     $map = array();
     foreach (array_chunk($need, 2048) as $chunk) {
         $rows = queryfx_all($conn_w, 'SELECT id, commitIdentifier FROM %T
       WHERE commitIdentifier IN (%Ls) AND repositoryID = %d', $commit_table_name, $chunk, $repo->getID());
         foreach ($rows as $row) {
             $map[$row['commitIdentifier']] = $row['id'];
         }
     }
     $insert_sql = array();
     $delete_sql = array();
     foreach ($graph as $child => $parents) {
         $names = $parents;
         $names[] = $child;
         foreach ($names as $name) {
             if (empty($map[$name])) {
                 throw new Exception(pht('Unknown commit "%s"!', $name));
             }
         }
         if (!$parents) {
             // Write an explicit 0 to indicate "no parents" instead of "no data".
             $insert_sql[] = qsprintf($conn_w, '(%d, 0)', $map[$child]);
         } else {
             foreach ($parents as $parent) {
                 $insert_sql[] = qsprintf($conn_w, '(%d, %d)', $map[$child], $map[$parent]);
             }
         }
         $delete_sql[] = $map[$child];
         $bar->update(1);
     }
     $commit_table->openTransaction();
     foreach (PhabricatorLiskDAO::chunkSQL($delete_sql) as $chunk) {
         queryfx($conn_w, 'DELETE FROM %T WHERE childCommitID IN (%Q)', PhabricatorRepository::TABLE_PARENTS, $chunk);
     }
     foreach (PhabricatorLiskDAO::chunkSQL($insert_sql) as $chunk) {
         queryfx($conn_w, 'INSERT INTO %T (childCommitID, parentCommitID) VALUES %Q', PhabricatorRepository::TABLE_PARENTS, $chunk);
     }
     $commit_table->saveTransaction();
     $bar->done();
 }
 /**
  * @task pull
  */
 private function resolveUpdateFuture(PhabricatorRepository $repository, ExecFuture $future, $min_sleep)
 {
     $monogram = $repository->getMonogram();
     $this->log(pht('Resolving update for "%s".', $monogram));
     try {
         list($stdout, $stderr) = $future->resolvex();
     } catch (Exception $ex) {
         $proxy = new PhutilProxyException(pht('Error while updating the "%s" repository.', $repository->getMonogram()), $ex);
         phlog($proxy);
         return time() + $min_sleep;
     }
     if (strlen($stderr)) {
         $stderr_msg = pht('Unexpected output while updating repository "%s": %s', $monogram, $stderr);
         phlog($stderr_msg);
     }
     $smart_wait = $repository->loadUpdateInterval($min_sleep);
     $this->log(pht('Based on activity in repository "%s", considering a wait of %s ' . 'seconds before update.', $repository->getMonogram(), new PhutilNumber($smart_wait)));
     return time() + $smart_wait;
 }
 /**
  * @task pull
  */
 private function resolveUpdateFuture(PhabricatorRepository $repository, ExecFuture $future, $min_sleep)
 {
     $monogram = $repository->getMonogram();
     $this->log(pht('Resolving update for "%s".', $monogram));
     try {
         list($stdout, $stderr) = $future->resolvex();
     } catch (Exception $ex) {
         $proxy = new PhutilProxyException(pht('Error while updating the "%s" repository.', $repository->getMonogram()), $ex);
         phlog($proxy);
         return time() + $min_sleep;
     }
     if (strlen($stderr)) {
         $stderr_msg = pht('Unexpected output while updating repository "%s": %s', $monogram, $stderr);
         phlog($stderr_msg);
     }
     // For now, continue respecting this deprecated setting for raising the
     // minimum pull frequency.
     // TODO: Remove this some day once this code has been completely stable
     // for a while.
     $sleep_for = (int) $repository->getDetail('pull-frequency');
     $min_sleep = max($sleep_for, $min_sleep);
     $smart_wait = $repository->loadUpdateInterval($min_sleep);
     $this->log(pht('Based on activity in repository "%s", considering a wait of %s ' . 'seconds before update.', $repository->getMonogram(), new PhutilNumber($smart_wait)));
     return time() + $smart_wait;
 }
 /**
  * @task pull
  */
 private function buildUpdateFuture(PhabricatorRepository $repository, $no_discovery)
 {
     $bin = dirname(phutil_get_library_root('phabricator')) . '/bin/repository';
     $flags = array();
     if ($no_discovery) {
         $flags[] = '--no-discovery';
     }
     $monogram = $repository->getMonogram();
     $future = new ExecFuture('%s update %Ls -- %s', $bin, $flags, $monogram);
     // Sometimes, the underlying VCS commands will hang indefinitely. We've
     // observed this occasionally with GitHub, and other users have observed
     // it with other VCS servers.
     // To limit the damage this can cause, kill the update out after a
     // reasonable amount of time, under the assumption that it has hung.
     // Since it's hard to know what a "reasonable" amount of time is given that
     // users may be downloading a repository full of pirated movies over a
     // potato, these limits are fairly generous. Repositories exceeding these
     // limits can be manually pulled with `bin/repository update X`, which can
     // just run for as long as it wants.
     if ($repository->isImporting()) {
         $timeout = phutil_units('4 hours in seconds');
     } else {
         $timeout = phutil_units('15 minutes in seconds');
     }
     $future->setTimeout($timeout);
     return $future;
 }