@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.

Increase clarity when closing a revision in response to a commit

Summary:
I am not sure how valuable this is *as is* - I think it needs different explanations for what happened in mercurial or subversion? I do not know what those explanations are.

Made an error in D10485 - the $hashes that were saved is an array of objects, so it ends up turning into garbage via the wonders of serialization and de-serialization. Fix that by explicitly saving the tree hash.

I would like to make this work for the other VCS types we support, add the "undo / nope" button and call it fixed.

Ref T3686.

Test Plan: clicked "explan why" and saw why

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: epriestley, Korvin

Maniphest Tasks: T5693, T3686

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

+206 -7
+2
src/__phutil_library_map__.php
··· 330 330 'DifferentialReviewersField' => 'applications/differential/customfield/DifferentialReviewersField.php', 331 331 'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php', 332 332 'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php', 333 + 'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php', 333 334 'DifferentialRevisionControlSystem' => 'applications/differential/constants/DifferentialRevisionControlSystem.php', 334 335 'DifferentialRevisionDetailView' => 'applications/differential/view/DifferentialRevisionDetailView.php', 335 336 'DifferentialRevisionEditController' => 'applications/differential/controller/DifferentialRevisionEditController.php', ··· 3223 3224 'PhabricatorDestructibleInterface', 3224 3225 'PhabricatorProjectInterface', 3225 3226 ), 3227 + 'DifferentialRevisionCloseDetailsController' => 'DifferentialController', 3226 3228 'DifferentialRevisionDetailView' => 'AphrontView', 3227 3229 'DifferentialRevisionEditController' => 'DifferentialController', 3228 3230 'DifferentialRevisionHasTaskEdgeType' => 'PhabricatorEdgeType',
+2
src/applications/differential/application/PhabricatorDifferentialApplication.php
··· 67 67 => 'DifferentialRevisionEditController', 68 68 'revision/land/(?:(?P<id>[1-9]\d*))/(?P<strategy>[^/]+)/' 69 69 => 'DifferentialRevisionLandController', 70 + 'revision/closedetails/(?P<phid>[^/]+)/' 71 + => 'DifferentialRevisionCloseDetailsController', 70 72 'comment/' => array( 71 73 'preview/(?P<id>[1-9]\d*)/' => 'DifferentialCommentPreviewController', 72 74 'save/(?P<id>[1-9]\d*)/' => 'DifferentialCommentSaveController',
+11
src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php
··· 81 81 } 82 82 } 83 83 84 + // grab some extra information about the Differential Revision: field... 85 + $revision_id_field = new DifferentialRevisionIDField(); 86 + $revision_id_value = idx( 87 + $corpus_map, 88 + $revision_id_field->getFieldKeyForConduit()); 89 + $revision_id_valid_domain = PhabricatorEnv::getProductionURI(''); 90 + 84 91 return array( 85 92 'errors' => $this->errors, 86 93 'fields' => $values, 94 + 'revisionIDFieldInfo' => array( 95 + 'value' => $revision_id_value, 96 + 'validDomain' => $revision_id_valid_domain, 97 + ), 87 98 ); 88 99 } 89 100
+113
src/applications/differential/controller/DifferentialRevisionCloseDetailsController.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionCloseDetailsController 4 + extends DifferentialController { 5 + 6 + private $phid; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->phid = idx($data, 'phid'); 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + 15 + $viewer = $request->getUser(); 16 + $xaction_phid = $this->phid; 17 + 18 + $xaction = id(new PhabricatorObjectQuery()) 19 + ->withPHIDs(array($xaction_phid)) 20 + ->setViewer($viewer) 21 + ->executeOne(); 22 + if (!$xaction) { 23 + return new Aphront404Response(); 24 + } 25 + 26 + $obj_phid = $xaction->getObjectPHID(); 27 + $obj_handle = id(new PhabricatorHandleQuery()) 28 + ->setViewer($viewer) 29 + ->withPHIDs(array($obj_phid)) 30 + ->executeOne(); 31 + 32 + $body = $this->getRevisionMatchExplanation( 33 + $xaction->getMetadataValue('revisionMatchData'), 34 + $obj_handle); 35 + 36 + $dialog = id(new AphrontDialogView()) 37 + ->setUser($viewer) 38 + ->setTitle(pht('Commit Close Explanation')) 39 + ->appendParagraph($body) 40 + ->addCancelButton($obj_handle->getURI()); 41 + 42 + return id(new AphrontDialogResponse())->setDialog($dialog); 43 + } 44 + 45 + private function getRevisionMatchExplanation( 46 + $revision_match_data, 47 + PhabricatorObjectHandle $obj_handle) { 48 + 49 + if (!$revision_match_data) { 50 + return pht( 51 + 'This commit was made before this feature was built and thus this '. 52 + 'information is unavailable.'); 53 + } 54 + 55 + $body_why = array(); 56 + if ($revision_match_data['usedURI']) { 57 + return pht( 58 + 'We found a "Differential Revision" field with value "%s" in the '. 59 + 'commit message, and the domain on the URI matches this install, so '. 60 + 'we linked this commit to %s.', 61 + $revision_match_data['foundURI'], 62 + phutil_tag( 63 + 'a', 64 + array( 65 + 'href' => $obj_handle->getURI(),), 66 + $obj_handle->getName())); 67 + } else if ($revision_match_data['foundURI']) { 68 + $body_why[] = pht( 69 + 'We found a "Differential Revision" field with value "%s" in the '. 70 + 'commit message, but the domain on this URI did not match the '. 71 + 'configured domain for this install, "%s", so we ignored it under '. 72 + 'the assumption that it refers to some third-party revision.', 73 + $revision_match_data['foundURI'], 74 + $revision_match_data['validDomain']); 75 + } else { 76 + $body_why[] = pht( 77 + 'We didn\'t find a "Differential Revision" field in the commit '. 78 + 'message.'); 79 + } 80 + 81 + switch ($revision_match_data['matchHashType']) { 82 + case ArcanistDifferentialRevisionHash::HASH_GIT_TREE: 83 + $hash_info = true; 84 + $hash_type = 'tree'; 85 + break; 86 + case ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT: 87 + case ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT: 88 + $hash_info = true; 89 + $hash_type = 'commit'; 90 + break; 91 + default: 92 + $hash_info = false; 93 + break; 94 + } 95 + if ($hash_info) { 96 + $diff_link = phutil_tag( 97 + 'a', 98 + array( 99 + 'href' => $obj_handle->getURI(),), 100 + $obj_handle->getName()); 101 + $body_why = pht( 102 + 'This commit and the active diff of %s had the same %s hash '. 103 + '(%s) so we linked this commit to %s.', 104 + $diff_link, 105 + $hash_type, 106 + $revision_match_data['matchHashValue'], 107 + $diff_link); 108 + } 109 + 110 + return phutil_implode_html("\n", $body_why); 111 + 112 + } 113 + }
+16
src/applications/differential/storage/DifferentialTransaction.php
··· 306 306 return parent::getTitle(); 307 307 } 308 308 309 + public function renderExtraInformationLink() { 310 + if ($this->getMetadataValue('revisionMatchData')) { 311 + $details_href = 312 + '/differential/revision/closedetails/'.$this->getPHID().'/'; 313 + $details_link = javelin_tag( 314 + 'a', 315 + array( 316 + 'href' => $details_href, 317 + 'sigil' => 'workflow', 318 + ), 319 + pht('Explain Why')); 320 + return $details_link; 321 + } 322 + return parent::renderExtraInformationLink(); 323 + } 324 + 309 325 public function getTitleForFeed(PhabricatorFeedStory $story) { 310 326 $author_phid = $this->getAuthorPHID(); 311 327 $object_phid = $this->getObjectPHID();
+49 -1
src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php
··· 4 4 extends DiffusionLowLevelQuery { 5 5 6 6 private $ref; 7 + private $revisionMatchData = array( 8 + 'usedURI' => null, 9 + 'foundURI' => null, 10 + 'validDomain' => null, 11 + 'matchHashType' => null, 12 + 'matchHashValue' => null, 13 + ); 7 14 8 15 public function withCommitRef(DiffusionCommitRef $ref) { 9 16 $this->ref = $ref; 10 17 return $this; 11 18 } 12 19 20 + public function getRevisionMatchData() { 21 + return $this->revisionMatchData; 22 + } 23 + 24 + private function setRevisionMatchData($key, $value) { 25 + $this->revisionMatchData[$key] = $value; 26 + return $this; 27 + } 28 + 13 29 public function executeQuery() { 14 30 $ref = $this->ref; 15 31 $message = $ref->getMessage(); ··· 25 41 ->execute(); 26 42 $fields = $result['fields']; 27 43 44 + $revision_id = idx($fields, 'revisionID'); 45 + if ($revision_id) { 46 + $this->setRevisionMatchData('usedURI', true); 47 + } else { 48 + $this->setRevisionMatchData('usedURI', false); 49 + } 50 + $revision_id_info = $result['revisionIDFieldInfo']; 51 + $this->setRevisionMatchData('foundURI', $revision_id_info['value']); 52 + $this->setRevisionMatchData( 53 + 'validDomain', 54 + $revision_id_info['validDomain']); 55 + 28 56 // If there is no "Differential Revision:" field in the message, try to 29 57 // identify the revision by doing a hash lookup. 30 58 31 - $revision_id = idx($fields, 'revisionID'); 32 59 if (!$revision_id && $hashes) { 33 60 $hash_list = array(); 34 61 foreach ($hashes as $hash) { ··· 36 63 } 37 64 $revisions = id(new DifferentialRevisionQuery()) 38 65 ->setViewer(PhabricatorUser::getOmnipotentUser()) 66 + ->needHashes(true) 39 67 ->withCommitHashes($hash_list) 40 68 ->execute(); 41 69 42 70 if (!empty($revisions)) { 43 71 $revision = $this->pickBestRevision($revisions); 44 72 $fields['revisionID'] = $revision->getID(); 73 + $revision_hashes = $revision->getHashes(); 74 + $revision_hashes = mpull($revision_hashes, 'getHashType'); 75 + // sort the hashes in the order the mighty 76 + // @{class:ArcanstDifferentialRevisionHash} does; probably unnecessary 77 + // but should future proof things nicely. 78 + $revision_hashes = array_select_keys( 79 + $revision_hashes, 80 + ArcanistDifferentialRevisionHash::getTypes()); 81 + foreach ($hashes as $hash) { 82 + $revision_hash = $revision_hashes[$hash->getHashType()]; 83 + if ($revision_hash->getHashValue() == $hash->getHashValue()) { 84 + $this->setRevisionMatchData( 85 + 'matchHashType', 86 + $hash->getHashType()); 87 + $this->setRevisionMatchData( 88 + 'matchHashValue', 89 + $hash->getHashValue()); 90 + break; 91 + } 92 + } 45 93 } 46 94 } 47 95
+13 -6
src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
··· 53 53 54 54 $differential_app = 'PhabricatorDifferentialApplication'; 55 55 $revision_id = null; 56 + $low_level_query = null; 56 57 if (PhabricatorApplication::isClassInstalled($differential_app)) { 57 - $field_values = id(new DiffusionLowLevelCommitFieldsQuery()) 58 + $low_level_query = id(new DiffusionLowLevelCommitFieldsQuery()) 58 59 ->setRepository($repository) 59 - ->withCommitRef($ref) 60 - ->execute(); 60 + ->withCommitRef($ref); 61 + $field_values = $low_level_query->execute(); 61 62 $revision_id = idx($field_values, 'revisionID'); 62 63 63 64 if (!empty($field_values['reviewedByPHIDs'])) { ··· 160 161 $commit_close_xaction->setMetadataValue( 161 162 'authorName', 162 163 $data->getAuthorName()); 163 - $commit_close_xaction->setMetadataValue( 164 - 'commitHashes', 165 - $hashes); 164 + 165 + if ($low_level_query) { 166 + $commit_close_xaction->setMetadataValue( 167 + 'revisionMatchData', 168 + $low_level_query->getRevisionMatchData()); 169 + $data->setCommitDetail( 170 + 'revisionMatchData', 171 + $low_level_query->getRevisionMatchData()); 172 + } 166 173 167 174 $diff = $this->generateFinalDiff($revision, $acting_as_phid); 168 175