[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionHistoryTableView extends DiffusionView { 4 5 private $history; 6 private $revisions = array(); 7 private $handles = array(); 8 private $isHead; 9 private $parents; 10 private $buildCache; 11 12 public function setHistory(array $history) { 13 assert_instances_of($history, 'DiffusionPathChange'); 14 $this->history = $history; 15 $this->buildCache = null; 16 return $this; 17 } 18 19 public function loadRevisions() { 20 $commit_phids = array(); 21 foreach ($this->history as $item) { 22 if ($item->getCommit()) { 23 $commit_phids[] = $item->getCommit()->getPHID(); 24 } 25 } 26 27 // TODO: Get rid of this. 28 $this->revisions = id(new DifferentialRevision()) 29 ->loadIDsByCommitPHIDs($commit_phids); 30 return $this; 31 } 32 33 public function setHandles(array $handles) { 34 assert_instances_of($handles, 'PhabricatorObjectHandle'); 35 $this->handles = $handles; 36 return $this; 37 } 38 39 public function getRequiredHandlePHIDs() { 40 $phids = array(); 41 foreach ($this->history as $item) { 42 $data = $item->getCommitData(); 43 if ($data) { 44 if ($data->getCommitDetail('authorPHID')) { 45 $phids[$data->getCommitDetail('authorPHID')] = true; 46 } 47 if ($data->getCommitDetail('committerPHID')) { 48 $phids[$data->getCommitDetail('committerPHID')] = true; 49 } 50 } 51 } 52 return array_keys($phids); 53 } 54 55 public function setParents(array $parents) { 56 $this->parents = $parents; 57 return $this; 58 } 59 60 public function setIsHead($is_head) { 61 $this->isHead = $is_head; 62 return $this; 63 } 64 65 public function loadBuildablesOnDemand() { 66 if ($this->buildCache !== null) { 67 return $this->buildCache; 68 } 69 70 $commits_to_builds = array(); 71 72 $commits = mpull($this->history, 'getCommit'); 73 74 $commit_phids = mpull($commits, 'getPHID'); 75 76 $buildables = id(new HarbormasterBuildableQuery()) 77 ->setViewer($this->getUser()) 78 ->withBuildablePHIDs($commit_phids) 79 ->withManualBuildables(false) 80 ->execute(); 81 82 $this->buildCache = mpull($buildables, null, 'getBuildablePHID'); 83 84 return $this->buildCache; 85 } 86 87 public function render() { 88 $drequest = $this->getDiffusionRequest(); 89 90 $handles = $this->handles; 91 92 $graph = null; 93 if ($this->parents) { 94 $graph = $this->renderGraph(); 95 } 96 97 $show_builds = PhabricatorApplication::isClassInstalledForViewer( 98 'PhabricatorHarbormasterApplication', 99 $this->getUser()); 100 101 $rows = array(); 102 $ii = 0; 103 foreach ($this->history as $history) { 104 $epoch = $history->getEpoch(); 105 106 if ($epoch) { 107 $date = phabricator_date($epoch, $this->user); 108 $time = phabricator_time($epoch, $this->user); 109 } else { 110 $date = null; 111 $time = null; 112 } 113 114 $data = $history->getCommitData(); 115 $author_phid = $committer = $committer_phid = null; 116 if ($data) { 117 $author_phid = $data->getCommitDetail('authorPHID'); 118 $committer_phid = $data->getCommitDetail('committerPHID'); 119 $committer = $data->getCommitDetail('committer'); 120 } 121 122 if ($author_phid && isset($handles[$author_phid])) { 123 $author = $handles[$author_phid]->renderLink(); 124 } else { 125 $author = self::renderName($history->getAuthorName()); 126 } 127 128 $different_committer = false; 129 if ($committer_phid) { 130 $different_committer = ($committer_phid != $author_phid); 131 } else if ($committer != '') { 132 $different_committer = ($committer != $history->getAuthorName()); 133 } 134 if ($different_committer) { 135 if ($committer_phid && isset($handles[$committer_phid])) { 136 $committer = $handles[$committer_phid]->renderLink(); 137 } else { 138 $committer = self::renderName($committer); 139 } 140 $author = hsprintf('%s/%s', $author, $committer); 141 } 142 143 // We can show details once the message and change have been imported. 144 $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | 145 PhabricatorRepositoryCommit::IMPORTED_CHANGE; 146 147 $commit = $history->getCommit(); 148 if ($commit && $commit->isPartiallyImported($partial_import) && $data) { 149 $summary = AphrontTableView::renderSingleDisplayLine( 150 $history->getSummary()); 151 } else { 152 $summary = phutil_tag('em', array(), "Importing\xE2\x80\xA6"); 153 } 154 155 $build = null; 156 if ($show_builds) { 157 $buildable_lookup = $this->loadBuildablesOnDemand(); 158 $buildable = idx($buildable_lookup, $commit->getPHID()); 159 if ($buildable !== null) { 160 $icon = HarbormasterBuildable::getBuildableStatusIcon( 161 $buildable->getBuildableStatus()); 162 $color = HarbormasterBuildable::getBuildableStatusColor( 163 $buildable->getBuildableStatus()); 164 $name = HarbormasterBuildable::getBuildableStatusName( 165 $buildable->getBuildableStatus()); 166 167 $icon_view = id(new PHUIIconView()) 168 ->setIconFont($icon.' '.$color); 169 170 $tooltip_view = javelin_tag( 171 'span', 172 array( 173 'sigil' => 'has-tooltip', 174 'meta' => array('tip' => $name), 175 ), 176 $icon_view); 177 178 Javelin::initBehavior('phabricator-tooltips'); 179 180 $href_view = phutil_tag( 181 'a', 182 array('href' => '/'.$buildable->getMonogram()), 183 $tooltip_view); 184 185 $build = $href_view; 186 187 $has_any_build = true; 188 } 189 } 190 191 $rows[] = array( 192 $graph ? $graph[$ii++] : null, 193 self::linkCommit( 194 $drequest->getRepository(), 195 $history->getCommitIdentifier()), 196 $build, 197 ($commit ? 198 self::linkRevision(idx($this->revisions, $commit->getPHID())) : 199 null), 200 $author, 201 $summary, 202 $date, 203 $time, 204 ); 205 } 206 207 $view = new AphrontTableView($rows); 208 $view->setHeaders( 209 array( 210 '', 211 pht('Commit'), 212 '', 213 pht('Revision'), 214 pht('Author/Committer'), 215 pht('Details'), 216 pht('Date'), 217 pht('Time'), 218 )); 219 $view->setColumnClasses( 220 array( 221 'threads', 222 'n', 223 'icon', 224 'n', 225 '', 226 'wide', 227 '', 228 'right', 229 )); 230 $view->setColumnVisibility( 231 array( 232 $graph ? true : false, 233 )); 234 $view->setDeviceVisibility( 235 array( 236 $graph ? true : false, 237 true, 238 true, 239 true, 240 false, 241 true, 242 false, 243 false, 244 )); 245 return $view->render(); 246 } 247 248 /** 249 * Draw a merge/branch graph from the parent revision data. We're basically 250 * building up a bunch of strings like this: 251 * 252 * ^ 253 * |^ 254 * o| 255 * |o 256 * o 257 * 258 * ...which form an ASCII representation of the graph we eventually want to 259 * draw. 260 * 261 * NOTE: The actual implementation is black magic. 262 */ 263 private function renderGraph() { 264 // This keeps our accumulated information about each line of the 265 // merge/branch graph. 266 $graph = array(); 267 268 // This holds the next commit we're looking for in each column of the 269 // graph. 270 $threads = array(); 271 272 // This is the largest number of columns any row has, i.e. the width of 273 // the graph. 274 $count = 0; 275 276 foreach ($this->history as $key => $history) { 277 $joins = array(); 278 $splits = array(); 279 280 $parent_list = $this->parents[$history->getCommitIdentifier()]; 281 282 // Look for some thread which has this commit as the next commit. If 283 // we find one, this commit goes on that thread. Otherwise, this commit 284 // goes on a new thread. 285 286 $line = ''; 287 $found = false; 288 $pos = count($threads); 289 for ($n = 0; $n < $count; $n++) { 290 if (empty($threads[$n])) { 291 $line .= ' '; 292 continue; 293 } 294 295 if ($threads[$n] == $history->getCommitIdentifier()) { 296 if ($found) { 297 $line .= ' '; 298 $joins[] = $n; 299 unset($threads[$n]); 300 } else { 301 $line .= 'o'; 302 $found = true; 303 $pos = $n; 304 } 305 } else { 306 307 // We render a "|" for any threads which have a commit that we haven't 308 // seen yet, this is later drawn as a vertical line. 309 $line .= '|'; 310 } 311 } 312 313 // If we didn't find the thread this commit goes on, start a new thread. 314 // We use "o" to mark the commit for the rendering engine, or "^" to 315 // indicate that there's nothing after it so the line from the commit 316 // upward should not be drawn. 317 318 if (!$found) { 319 if ($this->isHead) { 320 $line .= '^'; 321 } else { 322 $line .= 'o'; 323 foreach ($graph as $k => $meta) { 324 // Go back across all the lines we've already drawn and add a 325 // "|" to the end, since this is connected to some future commit 326 // we don't know about. 327 for ($jj = strlen($meta['line']); $jj <= $count; $jj++) { 328 $graph[$k]['line'] .= '|'; 329 } 330 } 331 } 332 } 333 334 // Update the next commit on this thread to the commit's first parent. 335 // This might have the effect of making a new thread. 336 $threads[$pos] = head($parent_list); 337 338 // If we made a new thread, increase the thread count. 339 $count = max($pos + 1, $count); 340 341 // Now, deal with splits (merges). I picked this terms opposite to the 342 // underlying repository term to confuse you. 343 foreach (array_slice($parent_list, 1) as $parent) { 344 $found = false; 345 346 // Try to find the other parent(s) in our existing threads. If we find 347 // them, split to that thread. 348 349 foreach ($threads as $idx => $thread_commit) { 350 if ($thread_commit == $parent) { 351 $found = true; 352 $splits[] = $idx; 353 } 354 } 355 356 // If we didn't find the parent, we don't know about it yet. Find the 357 // first free thread and add it as the "next" commit in that thread. 358 // This might create a new thread. 359 360 if (!$found) { 361 for ($n = 0; $n < $count; $n++) { 362 if (empty($threads[$n])) { 363 break; 364 } 365 } 366 $threads[$n] = $parent; 367 $splits[] = $n; 368 $count = max($n + 1, $count); 369 } 370 } 371 372 $graph[] = array( 373 'line' => $line, 374 'split' => $splits, 375 'join' => $joins, 376 ); 377 } 378 379 // Render into tags for the behavior. 380 381 foreach ($graph as $k => $meta) { 382 $graph[$k] = javelin_tag( 383 'div', 384 array( 385 'sigil' => 'commit-graph', 386 'meta' => $meta, 387 ), 388 ''); 389 } 390 391 Javelin::initBehavior( 392 'diffusion-commit-graph', 393 array( 394 'count' => $count, 395 )); 396 397 return $graph; 398 } 399 400 }
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 |