[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |