diff --git a/src/applications/differential/controller/DifferentialRevisionCloseDetailsController.php b/src/applications/differential/controller/DifferentialRevisionCloseDetailsController.php index d5a7d897a8..d405125c8e 100644 --- a/src/applications/differential/controller/DifferentialRevisionCloseDetailsController.php +++ b/src/applications/differential/controller/DifferentialRevisionCloseDetailsController.php @@ -1,108 +1,108 @@ getViewer(); $xaction = id(new PhabricatorObjectQuery()) ->withPHIDs(array($request->getURIData('phid'))) ->setViewer($viewer) ->executeOne(); if (!$xaction) { return new Aphront404Response(); } $obj_phid = $xaction->getObjectPHID(); $obj_handle = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs(array($obj_phid)) ->executeOne(); $body = $this->getRevisionMatchExplanation( $xaction->getMetadataValue('revisionMatchData'), $obj_handle); $dialog = id(new AphrontDialogView()) ->setUser($viewer) ->setTitle(pht('Commit Close Explanation')) ->appendParagraph($body) ->addCancelButton($obj_handle->getURI()); return id(new AphrontDialogResponse())->setDialog($dialog); } private function getRevisionMatchExplanation( $revision_match_data, PhabricatorObjectHandle $obj_handle) { if (!$revision_match_data) { return pht( 'This commit was made before this feature was built and thus this '. 'information is unavailable.'); } $body_why = array(); if ($revision_match_data['usedURI']) { return pht( 'We found a "%s" field with value "%s" in the commit message, '. 'and the domain on the URI matches this install, so '. 'we linked this commit to %s.', 'Differential Revision', $revision_match_data['foundURI'], phutil_tag( 'a', array( 'href' => $obj_handle->getURI(), ), $obj_handle->getName())); } else if ($revision_match_data['foundURI']) { $body_why[] = pht( 'We found a "%s" field with value "%s" in the commit message, '. 'but the domain on this URI did not match the configured '. 'domain for this install, "%s", so we ignored it under '. 'the assumption that it refers to some third-party revision.', 'Differential Revision', $revision_match_data['foundURI'], $revision_match_data['validDomain']); } else { $body_why[] = pht( 'We didn\'t find a "%s" field in the commit message.', 'Differential Revision'); } switch ($revision_match_data['matchHashType']) { case ArcanistDifferentialRevisionHash::HASH_GIT_TREE: $hash_info = true; $hash_type = 'tree'; break; case ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT: case ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT: $hash_info = true; $hash_type = 'commit'; break; default: $hash_info = false; break; } if ($hash_info) { $diff_link = phutil_tag( 'a', array( 'href' => $obj_handle->getURI(), ), $obj_handle->getName()); - $body_why = pht( + $body_why[] = pht( 'This commit and the active diff of %s had the same %s hash '. '(%s) so we linked this commit to %s.', $diff_link, $hash_type, $revision_match_data['matchHashValue'], $diff_link); } return phutil_implode_html("\n", $body_why); } } diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php index 7c7072dbf5..ab8f5e3e2e 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php @@ -1,139 +1,143 @@ null, 'foundURI' => null, 'validDomain' => null, 'matchHashType' => null, 'matchHashValue' => null, ); public function withCommitRef(DiffusionCommitRef $ref) { $this->ref = $ref; return $this; } public function getRevisionMatchData() { return $this->revisionMatchData; } private function setRevisionMatchData($key, $value) { $this->revisionMatchData[$key] = $value; return $this; } protected function executeQuery() { $ref = $this->ref; $message = $ref->getMessage(); $hashes = $ref->getHashes(); $params = array( 'corpus' => $message, 'partial' => true, ); $result = id(new ConduitCall('differential.parsecommitmessage', $params)) ->setUser(PhabricatorUser::getOmnipotentUser()) ->execute(); $fields = $result['fields']; $revision_id = idx($fields, 'revisionID'); if ($revision_id) { $this->setRevisionMatchData('usedURI', true); } else { $this->setRevisionMatchData('usedURI', false); } $revision_id_info = $result['revisionIDFieldInfo']; $this->setRevisionMatchData('foundURI', $revision_id_info['value']); $this->setRevisionMatchData( 'validDomain', $revision_id_info['validDomain']); // If there is no "Differential Revision:" field in the message, try to // identify the revision by doing a hash lookup. if (!$revision_id && $hashes) { $hash_list = array(); foreach ($hashes as $hash) { $hash_list[] = array($hash->getHashType(), $hash->getHashValue()); } $revisions = id(new DifferentialRevisionQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->needHashes(true) ->withCommitHashes($hash_list) ->execute(); - if (!empty($revisions)) { + if ($revisions) { $revision = $this->pickBestRevision($revisions); + $fields['revisionID'] = $revision->getID(); $revision_hashes = $revision->getHashes(); + $revision_hashes = DiffusionCommitHash::convertArrayToObjects( $revision_hashes); - $revision_hashes = mpull($revision_hashes, 'getHashType'); + $revision_hashes = mpull($revision_hashes, null, 'getHashType'); + // sort the hashes in the order the mighty // @{class:ArcanstDifferentialRevisionHash} does; probably unnecessary // but should future proof things nicely. $revision_hashes = array_select_keys( $revision_hashes, ArcanistDifferentialRevisionHash::getTypes()); + foreach ($hashes as $hash) { $revision_hash = idx($revision_hashes, $hash->getHashType()); if (!$revision_hash) { continue; } if ($revision_hash->getHashValue() == $hash->getHashValue()) { $this->setRevisionMatchData( 'matchHashType', $hash->getHashType()); $this->setRevisionMatchData( 'matchHashValue', $hash->getHashValue()); break; } } } } return $fields; } /** * When querying for revisions by hash, more than one revision may be found. * This function identifies the "best" revision from such a set. Typically, * there is only one revision found. Otherwise, we try to pick an accepted * revision first, followed by an open revision, and otherwise we go with a * closed or abandoned revision as a last resort. */ private function pickBestRevision(array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); // If we have more than one revision of a given status, choose the most // recently updated one. $revisions = msort($revisions, 'getDateModified'); $revisions = array_reverse($revisions); // Try to find an accepted revision first. $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; foreach ($revisions as $revision) { if ($revision->getStatus() == $status_accepted) { return $revision; } } // Try to find an open revision. foreach ($revisions as $revision) { if (!$revision->isClosed()) { return $revision; } } // Settle for whatever's left. return head($revisions); } }