public function execute(PhutilArgumentParser $args) { $commits = $this->loadCommits($args, 'commits'); if (!$commits) { throw new PhutilArgumentUsageException(pht('Specify one or more commits to resolve users for.')); } $console = PhutilConsole::getConsole(); foreach ($commits as $commit) { $repo = $commit->getRepository(); $name = $repo->formatCommitName($commit->getCommitIdentifier()); $console->writeOut("%s\n", pht('Examining commit %s...', $name)); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($this->getViewer(), DiffusionRequest::newFromDictionary(array('repository' => $repo, 'user' => $this->getViewer())), 'diffusion.querycommits', array('repositoryPHID' => $repo->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $author = $ref->getAuthor(); $console->writeOut("%s\n", pht('Raw author string: %s', coalesce($author, 'null'))); if ($author !== null) { $handle = $this->resolveUser($commit, $author); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } $committer = $ref->getCommitter(); $console->writeOut("%s\n", pht('Raw committer string: %s', coalesce($committer, 'null'))); if ($committer !== null) { $handle = $this->resolveUser($commit, $committer); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } } return 0; }
protected final function updateCommitData(DiffusionCommitRef $ref) { $commit = $this->commit; $author = $ref->getAuthor(); $message = $ref->getMessage(); $committer = $ref->getCommitter(); $hashes = $ref->getHashes(); $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID()); if (!$data) { $data = new PhabricatorRepositoryCommitData(); } $data->setCommitID($commit->getID()); $data->setAuthorName(id(new PhutilUTF8StringTruncator())->setMaximumBytes(255)->truncateString((string) $author)); $data->setCommitDetail('authorName', $ref->getAuthorName()); $data->setCommitDetail('authorEmail', $ref->getAuthorEmail()); $data->setCommitDetail('authorPHID', $this->resolveUserPHID($commit, $author)); $data->setCommitMessage($message); if (strlen($committer)) { $data->setCommitDetail('committer', $committer); $data->setCommitDetail('committerName', $ref->getCommitterName()); $data->setCommitDetail('committerEmail', $ref->getCommitterEmail()); $data->setCommitDetail('committerPHID', $this->resolveUserPHID($commit, $committer)); } $repository = $this->repository; $author_phid = $data->getCommitDetail('authorPHID'); $committer_phid = $data->getCommitDetail('committerPHID'); $user = new PhabricatorUser(); if ($author_phid) { $user = $user->loadOneWhere('phid = %s', $author_phid); } $differential_app = 'PhabricatorDifferentialApplication'; $revision_id = null; $low_level_query = null; if (PhabricatorApplication::isClassInstalled($differential_app)) { $low_level_query = id(new DiffusionLowLevelCommitFieldsQuery())->setRepository($repository)->withCommitRef($ref); $field_values = $low_level_query->execute(); $revision_id = idx($field_values, 'revisionID'); if (!empty($field_values['reviewedByPHIDs'])) { $data->setCommitDetail('reviewerPHID', reset($field_values['reviewedByPHIDs'])); } $data->setCommitDetail('differential.revisionID', $revision_id); } if ($author_phid != $commit->getAuthorPHID()) { $commit->setAuthorPHID($author_phid); } $commit->setSummary($data->getSummary()); $commit->save(); // Figure out if we're going to try to "autoclose" related objects (e.g., // close linked tasks and related revisions) and, if not, record why we // aren't. Autoclose can be disabled for various reasons at the repository // or commit levels. $force_autoclose = idx($this->getTaskData(), 'forceAutoclose', false); if ($force_autoclose) { $autoclose_reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; } else { $autoclose_reason = $repository->shouldSkipAutocloseCommit($commit); } $data->setCommitDetail('autocloseReason', $autoclose_reason); $should_autoclose = $force_autoclose || $repository->shouldAutocloseCommit($commit); // When updating related objects, we'll act under an omnipotent user to // ensure we can see them, but take actions as either the committer or // author (if we recognize their accounts) or the Diffusion application // (if we do not). $actor = PhabricatorUser::getOmnipotentUser(); $acting_as_phid = nonempty($committer_phid, $author_phid, id(new PhabricatorDiffusionApplication())->getPHID()); $conn_w = id(new DifferentialRevision())->establishConnection('w'); // NOTE: The `differential_commit` table has a unique ID on `commitPHID`, // preventing more than one revision from being associated with a commit. // Generally this is good and desirable, but with the advent of hash // tracking we may end up in a situation where we match several different // revisions. We just kind of ignore this and pick one, we might want to // revisit this and do something differently. (If we match several revisions // someone probably did something very silly, though.) $revision = null; if ($revision_id) { $revision_query = id(new DifferentialRevisionQuery())->withIDs(array($revision_id))->setViewer($actor)->needReviewerStatus(true)->needActiveDiffs(true); $revision = $revision_query->executeOne(); if ($revision) { if (!$data->getCommitDetail('precommitRevisionStatus')) { $data->setCommitDetail('precommitRevisionStatus', $revision->getStatus()); } $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST; id(new PhabricatorEdgeEditor())->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID())->save(); queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID()); $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; $should_close = $revision->getStatus() != $status_closed && $should_autoclose; if ($should_close) { $commit_close_xaction = id(new DifferentialTransaction())->setTransactionType(DifferentialTransaction::TYPE_ACTION)->setNewValue(DifferentialAction::ACTION_CLOSE)->setMetadataValue('isCommitClose', true); $commit_close_xaction->setMetadataValue('commitPHID', $commit->getPHID()); $commit_close_xaction->setMetadataValue('committerPHID', $committer_phid); $commit_close_xaction->setMetadataValue('committerName', $data->getCommitDetail('committer')); $commit_close_xaction->setMetadataValue('authorPHID', $author_phid); $commit_close_xaction->setMetadataValue('authorName', $data->getAuthorName()); if ($low_level_query) { $commit_close_xaction->setMetadataValue('revisionMatchData', $low_level_query->getRevisionMatchData()); $data->setCommitDetail('revisionMatchData', $low_level_query->getRevisionMatchData()); } $diff = $this->generateFinalDiff($revision, $acting_as_phid); $vs_diff = $this->loadChangedByCommit($revision, $diff); $changed_uri = null; if ($vs_diff) { $data->setCommitDetail('vsDiff', $vs_diff->getID()); $changed_uri = PhabricatorEnv::getProductionURI('/D' . $revision->getID() . '?vs=' . $vs_diff->getID() . '&id=' . $diff->getID() . '#toc'); } $xactions = array(); $xactions[] = id(new DifferentialTransaction())->setTransactionType(DifferentialTransaction::TYPE_UPDATE)->setIgnoreOnNoEffect(true)->setNewValue($diff->getPHID())->setMetadataValue('isCommitUpdate', true); $xactions[] = $commit_close_xaction; $content_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_DAEMON, array()); $editor = id(new DifferentialTransactionEditor())->setActor($actor)->setActingAsPHID($acting_as_phid)->setContinueOnMissingFields(true)->setContentSource($content_source)->setChangedPriorToCommitURI($changed_uri)->setIsCloseByCommit(true); try { $editor->applyTransactions($revision, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { // NOTE: We've marked transactions other than the CLOSE transaction // as ignored when they don't have an effect, so this means that we // lost a race to close the revision. That's perfectly fine, we can // just continue normally. } } } } if ($should_autoclose) { $this->closeTasks($actor, $acting_as_phid, $repository, $commit, $message); } $data->save(); $commit->writeImportStatusFlag(PhabricatorRepositoryCommit::IMPORTED_MESSAGE); }