@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Perform derived index updates in TransactionEditor

Summary: Ref T2222. We have two tables (one for hashes; one for paths) which were unevenly updated before. Now, update them consistently in the TransactionEditor.

Test Plan: Created a revision, saw it populate this information.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2222

Differential Revision: https://secure.phabricator.com/D8455

+214 -259
-236
src/applications/differential/editor/DifferentialRevisionEditor.php
··· 18 18 private $auxiliaryFields = array(); 19 19 private $contentSource; 20 20 private $isCreate; 21 - private $aphrontRequestForEventDispatch; 22 - 23 - 24 - public function setAphrontRequestForEventDispatch(AphrontRequest $request) { 25 - $this->aphrontRequestForEventDispatch = $request; 26 - return $this; 27 - } 28 - 29 - public function getAphrontRequestForEventDispatch() { 30 - return $this->aphrontRequestForEventDispatch; 31 - } 32 21 33 22 public function __construct(DifferentialRevision $revision) { 34 23 $this->revision = $revision; 35 24 $this->isCreate = !($revision->getID()); 36 25 } 37 26 38 - public static function newRevisionFromConduitWithDiff( 39 - array $fields, 40 - DifferentialDiff $diff, 41 - PhabricatorUser $actor) { 42 - 43 - $revision = DifferentialRevision::initializeNewRevision($actor); 44 - $revision->setPHID($revision->generatePHID()); 45 - 46 - $editor = new DifferentialRevisionEditor($revision); 47 - $editor->setActor($actor); 48 - $editor->addDiff($diff, null); 49 - $editor->copyFieldsFromConduit($fields); 50 - 51 - $editor->save(); 52 - 53 - return $revision; 54 - } 55 - 56 - public function copyFieldsFromConduit(array $fields) { 57 - 58 - $actor = $this->getActor(); 59 - $revision = $this->revision; 60 - $revision->loadRelationships(); 61 - 62 - $all_fields = DifferentialFieldSelector::newSelector() 63 - ->getFieldSpecifications(); 64 - 65 - $aux_fields = array(); 66 - foreach ($all_fields as $aux_field) { 67 - $aux_field->setRevision($revision); 68 - $aux_field->setDiff($this->diff); 69 - $aux_field->setUser($actor); 70 - if ($aux_field->shouldAppearOnCommitMessage()) { 71 - $aux_fields[$aux_field->getCommitMessageKey()] = $aux_field; 72 - } 73 - } 74 - 75 - foreach ($fields as $field => $value) { 76 - if (empty($aux_fields[$field])) { 77 - throw new Exception( 78 - "Parsed commit message contains unrecognized field '{$field}'."); 79 - } 80 - $aux_fields[$field]->setValueFromParsedCommitMessage($value); 81 - } 82 - 83 - foreach ($aux_fields as $aux_field) { 84 - $aux_field->validateField(); 85 - } 86 - 87 - $this->setAuxiliaryFields($all_fields); 88 - } 89 - 90 27 public function setAuxiliaryFields(array $auxiliary_fields) { 91 28 assert_instances_of($auxiliary_fields, 'DifferentialFieldSpecification'); 92 29 $this->auxiliaryFields = $auxiliary_fields; ··· 399 336 $diff->setDescription(preg_replace('/\n.*/s', '', $this->getComments())); 400 337 $diff->save(); 401 338 402 - $this->updateAffectedPathTable($revision, $diff, $changesets); 403 - $this->updateRevisionHashTable($revision, $diff); 404 - 405 339 // An updated diff should require review, as long as it's not closed 406 340 // or accepted. The "accepted" status is "sticky" to encourage courtesy 407 341 // re-diffs after someone accepts with minor changes/suggestions. ··· 686 620 } 687 621 } 688 622 689 - /** 690 - * Update the table which links Differential revisions to paths they affect, 691 - * so Diffusion can efficiently find pending revisions for a given file. 692 - */ 693 - private function updateAffectedPathTable( 694 - DifferentialRevision $revision, 695 - DifferentialDiff $diff, 696 - array $changesets) { 697 - assert_instances_of($changesets, 'DifferentialChangeset'); 698 623 699 - $project = $diff->loadArcanistProject(); 700 - if (!$project) { 701 - // Probably an old revision from before projects. 702 - return; 703 - } 704 - 705 - $repository = $project->loadRepository(); 706 - if (!$repository) { 707 - // Probably no project <-> repository link, or the repository where the 708 - // project lives is untracked. 709 - return; 710 - } 711 - 712 - $path_prefix = null; 713 - 714 - $local_root = $diff->getSourceControlPath(); 715 - if ($local_root) { 716 - // We're in a working copy which supports subdirectory checkouts (e.g., 717 - // SVN) so we need to figure out what prefix we should add to each path 718 - // (e.g., trunk/projects/example/) to get the absolute path from the 719 - // root of the repository. DVCS systems like Git and Mercurial are not 720 - // affected. 721 - 722 - // Normalize both paths and check if the repository root is a prefix of 723 - // the local root. If so, throw it away. Note that this correctly handles 724 - // the case where the remote path is "/". 725 - $local_root = id(new PhutilURI($local_root))->getPath(); 726 - $local_root = rtrim($local_root, '/'); 727 - 728 - $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath(); 729 - $repo_root = rtrim($repo_root, '/'); 730 - 731 - if (!strncmp($repo_root, $local_root, strlen($repo_root))) { 732 - $path_prefix = substr($local_root, strlen($repo_root)); 733 - } 734 - } 735 - 736 - $paths = array(); 737 - foreach ($changesets as $changeset) { 738 - $paths[] = $path_prefix.'/'.$changeset->getFilename(); 739 - } 740 - 741 - // Mark this as also touching all parent paths, so you can see all pending 742 - // changes to any file within a directory. 743 - $all_paths = array(); 744 - foreach ($paths as $local) { 745 - foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) { 746 - $all_paths[$path] = true; 747 - } 748 - } 749 - $all_paths = array_keys($all_paths); 750 - 751 - $path_ids = 752 - PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths( 753 - $all_paths); 754 - 755 - $table = new DifferentialAffectedPath(); 756 - $conn_w = $table->establishConnection('w'); 757 - 758 - $sql = array(); 759 - foreach ($path_ids as $path_id) { 760 - $sql[] = qsprintf( 761 - $conn_w, 762 - '(%d, %d, %d, %d)', 763 - $repository->getID(), 764 - $path_id, 765 - time(), 766 - $revision->getID()); 767 - } 768 - 769 - queryfx( 770 - $conn_w, 771 - 'DELETE FROM %T WHERE revisionID = %d', 772 - $table->getTableName(), 773 - $revision->getID()); 774 - foreach (array_chunk($sql, 256) as $chunk) { 775 - queryfx( 776 - $conn_w, 777 - 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q', 778 - $table->getTableName(), 779 - implode(', ', $chunk)); 780 - } 781 - } 782 - 783 - 784 - /** 785 - * Update the table connecting revisions to DVCS local hashes, so we can 786 - * identify revisions by commit/tree hashes. 787 - */ 788 - private function updateRevisionHashTable( 789 - DifferentialRevision $revision, 790 - DifferentialDiff $diff) { 791 - 792 - $vcs = $diff->getSourceControlSystem(); 793 - if ($vcs == DifferentialRevisionControlSystem::SVN) { 794 - // Subversion has no local commit or tree hash information, so we don't 795 - // have to do anything. 796 - return; 797 - } 798 - 799 - $property = id(new DifferentialDiffProperty())->loadOneWhere( 800 - 'diffID = %d AND name = %s', 801 - $diff->getID(), 802 - 'local:commits'); 803 - if (!$property) { 804 - return; 805 - } 806 - 807 - $hashes = array(); 808 - 809 - $data = $property->getData(); 810 - switch ($vcs) { 811 - case DifferentialRevisionControlSystem::GIT: 812 - foreach ($data as $commit) { 813 - $hashes[] = array( 814 - ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT, 815 - $commit['commit'], 816 - ); 817 - $hashes[] = array( 818 - ArcanistDifferentialRevisionHash::HASH_GIT_TREE, 819 - $commit['tree'], 820 - ); 821 - } 822 - break; 823 - case DifferentialRevisionControlSystem::MERCURIAL: 824 - foreach ($data as $commit) { 825 - $hashes[] = array( 826 - ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT, 827 - $commit['rev'], 828 - ); 829 - } 830 - break; 831 - } 832 - 833 - $conn_w = $revision->establishConnection('w'); 834 - 835 - $sql = array(); 836 - foreach ($hashes as $info) { 837 - list($type, $hash) = $info; 838 - $sql[] = qsprintf( 839 - $conn_w, 840 - '(%d, %s, %s)', 841 - $revision->getID(), 842 - $type, 843 - $hash); 844 - } 845 - 846 - queryfx( 847 - $conn_w, 848 - 'DELETE FROM %T WHERE revisionID = %d', 849 - ArcanistDifferentialRevisionHash::TABLE_NAME, 850 - $revision->getID()); 851 - 852 - if ($sql) { 853 - queryfx( 854 - $conn_w, 855 - 'INSERT INTO %T (revisionID, type, hash) VALUES %Q', 856 - ArcanistDifferentialRevisionHash::TABLE_NAME, 857 - implode(', ', $sql)); 858 - } 859 - } 860 624 861 625 /** 862 626 * Try to move a revision to "accepted". We look for:
+214 -23
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 276 276 $maniphest = 'PhabricatorApplicationManiphest'; 277 277 if (PhabricatorApplication::isClassInstalled($maniphest)) { 278 278 $diff = $this->loadDiff($xaction->getNewValue()); 279 - if ($diff) { 280 - $branch = $diff->getBranch(); 279 + $branch = $diff->getBranch(); 281 280 282 - // No "$", to allow for branches like T123_demo. 283 - $match = null; 284 - if (preg_match('/^T(\d+)/i', $branch, $match)) { 285 - $task_id = $match[1]; 286 - $tasks = id(new ManiphestTaskQuery()) 287 - ->setViewer($this->getActor()) 288 - ->withIDs(array($task_id)) 289 - ->execute(); 290 - if ($tasks) { 291 - $task = head($tasks); 292 - $task_phid = $task->getPHID(); 281 + // No "$", to allow for branches like T123_demo. 282 + $match = null; 283 + if (preg_match('/^T(\d+)/i', $branch, $match)) { 284 + $task_id = $match[1]; 285 + $tasks = id(new ManiphestTaskQuery()) 286 + ->setViewer($this->getActor()) 287 + ->withIDs(array($task_id)) 288 + ->execute(); 289 + if ($tasks) { 290 + $task = head($tasks); 291 + $task_phid = $task->getPHID(); 293 292 294 - $results[] = id(new DifferentialTransaction()) 295 - ->setTransactionType($type_edge) 296 - ->setMetadataValue('edge:type', $edge_ref_task) 297 - ->setIgnoreOnNoEffect(true) 298 - ->setNewValue(array('+' => array($task_phid => $task_phid))); 299 - } 293 + $results[] = id(new DifferentialTransaction()) 294 + ->setTransactionType($type_edge) 295 + ->setMetadataValue('edge:type', $edge_ref_task) 296 + ->setIgnoreOnNoEffect(true) 297 + ->setNewValue(array('+' => array($task_phid => $task_phid))); 300 298 } 301 299 } 302 300 } ··· 505 503 protected function applyFinalEffects( 506 504 PhabricatorLiskDAO $object, 507 505 array $xactions) { 506 + 507 + foreach ($xactions as $xaction) { 508 + switch ($xaction->getTransactionType()) { 509 + case DifferentialTransaction::TYPE_UPDATE: 510 + $diff = $this->loadDiff($xaction->getNewValue(), true); 511 + 512 + // Update these denormalized index tables when we attach a new 513 + // diff to a revision. 514 + 515 + $this->updateRevisionHashTable($object, $diff); 516 + $this->updateAffectedPathTable($object, $diff); 517 + break; 518 + } 519 + } 508 520 509 521 $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; 510 522 $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; ··· 1167 1179 return implode("\n", $result); 1168 1180 } 1169 1181 1170 - private function loadDiff($phid) { 1171 - return id(new DifferentialDiffQuery()) 1182 + private function loadDiff($phid, $need_changesets = false) { 1183 + $query = id(new DifferentialDiffQuery()) 1172 1184 ->withPHIDs(array($phid)) 1173 - ->setViewer($this->getActor()) 1174 - ->executeOne(); 1185 + ->setViewer($this->getActor()); 1186 + 1187 + if ($need_changesets) { 1188 + $query->needChangesets(true); 1189 + } 1190 + 1191 + return $query->executeOne(); 1175 1192 } 1176 1193 1177 1194 /* -( Herald Integration )------------------------------------------------- */ ··· 1298 1315 $adapter->getBuildPlans()); 1299 1316 1300 1317 return $xactions; 1318 + } 1319 + 1320 + /** 1321 + * Update the table which links Differential revisions to paths they affect, 1322 + * so Diffusion can efficiently find pending revisions for a given file. 1323 + */ 1324 + private function updateAffectedPathTable( 1325 + DifferentialRevision $revision, 1326 + DifferentialDiff $diff) { 1327 + 1328 + $changesets = $diff->getChangesets(); 1329 + 1330 + // TODO: This all needs to be modernized. 1331 + 1332 + $project = $diff->loadArcanistProject(); 1333 + if (!$project) { 1334 + // Probably an old revision from before projects. 1335 + return; 1336 + } 1337 + 1338 + $repository = $project->loadRepository(); 1339 + if (!$repository) { 1340 + // Probably no project <-> repository link, or the repository where the 1341 + // project lives is untracked. 1342 + return; 1343 + } 1344 + 1345 + $path_prefix = null; 1346 + 1347 + $local_root = $diff->getSourceControlPath(); 1348 + if ($local_root) { 1349 + // We're in a working copy which supports subdirectory checkouts (e.g., 1350 + // SVN) so we need to figure out what prefix we should add to each path 1351 + // (e.g., trunk/projects/example/) to get the absolute path from the 1352 + // root of the repository. DVCS systems like Git and Mercurial are not 1353 + // affected. 1354 + 1355 + // Normalize both paths and check if the repository root is a prefix of 1356 + // the local root. If so, throw it away. Note that this correctly handles 1357 + // the case where the remote path is "/". 1358 + $local_root = id(new PhutilURI($local_root))->getPath(); 1359 + $local_root = rtrim($local_root, '/'); 1360 + 1361 + $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath(); 1362 + $repo_root = rtrim($repo_root, '/'); 1363 + 1364 + if (!strncmp($repo_root, $local_root, strlen($repo_root))) { 1365 + $path_prefix = substr($local_root, strlen($repo_root)); 1366 + } 1367 + } 1368 + 1369 + $paths = array(); 1370 + foreach ($changesets as $changeset) { 1371 + $paths[] = $path_prefix.'/'.$changeset->getFilename(); 1372 + } 1373 + 1374 + // Mark this as also touching all parent paths, so you can see all pending 1375 + // changes to any file within a directory. 1376 + $all_paths = array(); 1377 + foreach ($paths as $local) { 1378 + foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) { 1379 + $all_paths[$path] = true; 1380 + } 1381 + } 1382 + $all_paths = array_keys($all_paths); 1383 + 1384 + $path_ids = 1385 + PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths( 1386 + $all_paths); 1387 + 1388 + $table = new DifferentialAffectedPath(); 1389 + $conn_w = $table->establishConnection('w'); 1390 + 1391 + $sql = array(); 1392 + foreach ($path_ids as $path_id) { 1393 + $sql[] = qsprintf( 1394 + $conn_w, 1395 + '(%d, %d, %d, %d)', 1396 + $repository->getID(), 1397 + $path_id, 1398 + time(), 1399 + $revision->getID()); 1400 + } 1401 + 1402 + queryfx( 1403 + $conn_w, 1404 + 'DELETE FROM %T WHERE revisionID = %d', 1405 + $table->getTableName(), 1406 + $revision->getID()); 1407 + foreach (array_chunk($sql, 256) as $chunk) { 1408 + queryfx( 1409 + $conn_w, 1410 + 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q', 1411 + $table->getTableName(), 1412 + implode(', ', $chunk)); 1413 + } 1414 + } 1415 + 1416 + 1417 + /** 1418 + * Update the table connecting revisions to DVCS local hashes, so we can 1419 + * identify revisions by commit/tree hashes. 1420 + */ 1421 + private function updateRevisionHashTable( 1422 + DifferentialRevision $revision, 1423 + DifferentialDiff $diff) { 1424 + 1425 + $vcs = $diff->getSourceControlSystem(); 1426 + if ($vcs == DifferentialRevisionControlSystem::SVN) { 1427 + // Subversion has no local commit or tree hash information, so we don't 1428 + // have to do anything. 1429 + return; 1430 + } 1431 + 1432 + $property = id(new DifferentialDiffProperty())->loadOneWhere( 1433 + 'diffID = %d AND name = %s', 1434 + $diff->getID(), 1435 + 'local:commits'); 1436 + if (!$property) { 1437 + return; 1438 + } 1439 + 1440 + $hashes = array(); 1441 + 1442 + $data = $property->getData(); 1443 + switch ($vcs) { 1444 + case DifferentialRevisionControlSystem::GIT: 1445 + foreach ($data as $commit) { 1446 + $hashes[] = array( 1447 + ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT, 1448 + $commit['commit'], 1449 + ); 1450 + $hashes[] = array( 1451 + ArcanistDifferentialRevisionHash::HASH_GIT_TREE, 1452 + $commit['tree'], 1453 + ); 1454 + } 1455 + break; 1456 + case DifferentialRevisionControlSystem::MERCURIAL: 1457 + foreach ($data as $commit) { 1458 + $hashes[] = array( 1459 + ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT, 1460 + $commit['rev'], 1461 + ); 1462 + } 1463 + break; 1464 + } 1465 + 1466 + $conn_w = $revision->establishConnection('w'); 1467 + 1468 + $sql = array(); 1469 + foreach ($hashes as $info) { 1470 + list($type, $hash) = $info; 1471 + $sql[] = qsprintf( 1472 + $conn_w, 1473 + '(%d, %s, %s)', 1474 + $revision->getID(), 1475 + $type, 1476 + $hash); 1477 + } 1478 + 1479 + queryfx( 1480 + $conn_w, 1481 + 'DELETE FROM %T WHERE revisionID = %d', 1482 + ArcanistDifferentialRevisionHash::TABLE_NAME, 1483 + $revision->getID()); 1484 + 1485 + if ($sql) { 1486 + queryfx( 1487 + $conn_w, 1488 + 'INSERT INTO %T (revisionID, type, hash) VALUES %Q', 1489 + ArcanistDifferentialRevisionHash::TABLE_NAME, 1490 + implode(', ', $sql)); 1491 + } 1301 1492 } 1302 1493 1303 1494 }