[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/repository/daemon/ -> PhabricatorMercurialGraphStream.php (source)

   1  <?php
   2  
   3  /**
   4   * Streaming interface on top of "hg log" that gives us performant access to
   5   * the Mercurial commit graph with one nonblocking invocation of "hg". See
   6   * @{class:PhabricatorRepositoryPullLocalDaemon}.
   7   */
   8  final class PhabricatorMercurialGraphStream
   9    extends PhabricatorRepositoryGraphStream {
  10  
  11    private $repository;
  12    private $iterator;
  13  
  14    private $parents        = array();
  15    private $dates          = array();
  16    private $local          = array();
  17    private $localParents   = array();
  18  
  19    public function __construct(PhabricatorRepository $repository, $commit) {
  20      $this->repository = $repository;
  21  
  22      $future = $repository->getLocalCommandFuture(
  23        'log --template %s --rev %s',
  24        '{rev}\1{node}\1{date}\1{parents}\2',
  25        hgsprintf('reverse(ancestors(%s))', $commit));
  26  
  27      $this->iterator = new LinesOfALargeExecFuture($future);
  28      $this->iterator->setDelimiter("\2");
  29      $this->iterator->rewind();
  30    }
  31  
  32    public function getParents($commit) {
  33      if (!isset($this->parents[$commit])) {
  34        $this->parseUntil('node', $commit);
  35  
  36        $local = $this->localParents[$commit];
  37  
  38        // The normal parsing pass gives us the local revision numbers of the
  39        // parents, but since we've decided we care about this data, we need to
  40        // convert them into full hashes. To do this, we parse to the deepest
  41        // one and then just look them up.
  42  
  43        $parents = array();
  44        if ($local) {
  45          $this->parseUntil('rev', min($local));
  46          foreach ($local as $rev) {
  47            $parents[] = $this->local[$rev];
  48          }
  49        }
  50  
  51        $this->parents[$commit] = $parents;
  52  
  53        // Throw away the local info for this commit, we no longer need it.
  54        unset($this->localParents[$commit]);
  55      }
  56  
  57      return $this->parents[$commit];
  58    }
  59  
  60    public function getCommitDate($commit) {
  61      if (!isset($this->dates[$commit])) {
  62        $this->parseUntil('node', $commit);
  63      }
  64      return $this->dates[$commit];
  65    }
  66  
  67    /**
  68     * Parse until we have consumed some object. There are two types of parses:
  69     * parse until we find a commit hash ($until_type = "node"), or parse until we
  70     * find a local commit number ($until_type = "rev"). We use the former when
  71     * looking up commits, and the latter when resolving parents.
  72     */
  73    private function parseUntil($until_type, $until_name) {
  74      if ($this->isParsed($until_type, $until_name)) {
  75        return;
  76      }
  77  
  78      $hglog = $this->iterator;
  79  
  80      while ($hglog->valid()) {
  81        $line = $hglog->current();
  82        $hglog->next();
  83  
  84        $line = trim($line);
  85        if (!strlen($line)) {
  86          break;
  87        }
  88        list($rev, $node, $date, $parents) = explode("\1", $line);
  89  
  90        $rev  = (int)$rev;
  91        $date = (int)head(explode('.', $date));
  92  
  93        $this->dates[$node]        = $date;
  94        $this->local[$rev]         = $node;
  95        $this->localParents[$node] = $this->parseParents($parents, $rev);
  96  
  97        if ($this->isParsed($until_type, $until_name)) {
  98          return;
  99        }
 100      }
 101  
 102      throw new Exception(
 103        "No such {$until_type} '{$until_name}' in repository!");
 104    }
 105  
 106  
 107    /**
 108     * Parse a {parents} template, returning the local commit numbers.
 109     */
 110    private function parseParents($parents, $target_rev) {
 111  
 112      // The hg '{parents}' token is empty if there is one "natural" parent
 113      // (predecessor local commit ID). Othwerwise, it may have one or two
 114      // parents. The string looks like this:
 115      //
 116      //  151:1f6c61a60586 154:1d5f799ebe1e
 117  
 118      $parents = trim($parents);
 119      if (strlen($parents)) {
 120        $local = array();
 121  
 122        $parents = explode(' ', $parents);
 123        foreach ($parents as $key => $parent) {
 124          $parent = (int)head(explode(':', $parent));
 125          if ($parent == -1) {
 126            // Initial commits will sometimes have "-1" as a parent.
 127            continue;
 128          }
 129          $local[] = $parent;
 130        }
 131      } else if ($target_rev) {
 132        // We have empty parents. If there's a predecessor, that's the local
 133        // parent number.
 134        $local = array($target_rev - 1);
 135      } else {
 136        // Initial commits will sometimes have no parents.
 137        $local = array();
 138      }
 139  
 140      return $local;
 141    }
 142  
 143  
 144    /**
 145     * Returns true if the object specified by $type ('rev' or 'node') and
 146     * $name (rev or node name) has been consumed from the hg process.
 147     */
 148    private function isParsed($type, $name) {
 149      switch ($type) {
 150        case 'rev':
 151          return isset($this->local[$name]);
 152        case 'node':
 153          return isset($this->dates[$name]);
 154      }
 155    }
 156  
 157  
 158  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1