diff --git a/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php b/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php index d832a1ac95..8cd8a4bfa2 100644 --- a/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php +++ b/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php @@ -1,106 +1,120 @@ viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setSourcePHID($source_phid) { $this->sourcePHID = $source_phid; return $this; } public function getSourcePHID() { if (!$this->sourcePHID) { throw new PhutilInvalidStateException('setSourcePHID'); } return $this->sourcePHID; } + public function setDryRun($dry_run) { + $this->dryRun = $dry_run; + return $this; + } + + public function getDryRun() { + return $this->dryRun; + } + public function newResolvedIdentity($raw_identity) { $identity = $this->loadRawIdentity($raw_identity); if (!$identity) { $identity = $this->newIdentity($raw_identity); } return $this->updateIdentity($identity); } public function newUpdatedIdentity(PhabricatorRepositoryIdentity $identity) { return $this->updateIdentity($identity); } private function loadRawIdentity($raw_identity) { $viewer = $this->getViewer(); return id(new PhabricatorRepositoryIdentityQuery()) ->setViewer($viewer) ->withIdentityNames(array($raw_identity)) ->executeOne(); } private function newIdentity($raw_identity) { $source_phid = $this->getSourcePHID(); return id(new PhabricatorRepositoryIdentity()) ->setAuthorPHID($source_phid) ->setIdentityName($raw_identity); } private function resolveIdentity(PhabricatorRepositoryIdentity $identity) { $raw_identity = $identity->getIdentityName(); return id(new DiffusionResolveUserQuery()) ->withName($raw_identity) ->execute(); } private function updateIdentity(PhabricatorRepositoryIdentity $identity) { // If we're updating an identity and it has a manual user PHID associated // with it but the user is no longer valid, remove the value. This likely // corresponds to a user that was destroyed. $assigned_phid = $identity->getManuallySetUserPHID(); $unassigned = DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN; if ($assigned_phid && ($assigned_phid !== $unassigned)) { $viewer = $this->getViewer(); $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withPHIDs(array($assigned_phid)) ->executeOne(); if (!$user) { $identity->setManuallySetUserPHID(null); } } $resolved_phid = $this->resolveIdentity($identity); - $identity - ->setAutomaticGuessedUserPHID($resolved_phid) - ->save(); + $identity->setAutomaticGuessedUserPHID($resolved_phid); + + if ($this->getDryRun()) { + $identity->makeEphemeral(); + } else { + $identity->save(); + } return $identity; } public function didUpdateEmailAddress($raw_address) { PhabricatorWorker::scheduleTask( 'PhabricatorRepositoryIdentityChangeWorker', array( 'emailAddresses' => array($raw_address), )); } } diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php index 513d7a3805..bd906aa5da 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php @@ -1,388 +1,433 @@ setName('rebuild-identities') ->setExamples( '**rebuild-identities** [__options__] __repository__') ->setSynopsis(pht('Rebuild repository identities from commits.')) ->setArguments( array( array( 'name' => 'all-repositories', 'help' => pht('Rebuild identities across all repositories.'), ), array( 'name' => 'all-identities', 'help' => pht('Rebuild all currently-known identities.'), ), array( 'name' => 'repository', 'param' => 'repository', 'repeat' => true, 'help' => pht('Rebuild identities in a repository.'), ), array( 'name' => 'commit', 'param' => 'commit', 'repeat' => true, 'help' => pht('Rebuild identities for a commit.'), ), array( 'name' => 'user', 'param' => 'user', 'repeat' => true, 'help' => pht('Rebuild identities for a user.'), ), array( 'name' => 'email', 'param' => 'email', 'repeat' => true, 'help' => pht('Rebuild identities for an email address.'), ), array( 'name' => 'raw', 'param' => 'raw', 'repeat' => true, 'help' => pht('Rebuild identities for a raw commit string.'), ), + array( + 'name' => 'dry-run', + 'help' => pht('Show changes, but do not make any changes.'), + ), )); } public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $rebuilt_anything = false; - $all_repositories = $args->getArg('all-repositories'); $repositories = $args->getArg('repository'); if ($all_repositories && $repositories) { throw new PhutilArgumentUsageException( pht( 'Flags "--all-repositories" and "--repository" are not '. 'compatible.')); } $all_identities = $args->getArg('all-identities'); $raw = $args->getArg('raw'); if ($all_identities && $raw) { throw new PhutilArgumentUsageException( pht( 'Flags "--all-identities" and "--raw" are not '. 'compatible.')); } + $dry_run = $args->getArg('dry-run'); + $this->dryRun = $dry_run; + + if ($this->dryRun) { + $this->logWarn( + pht('DRY RUN'), + pht('This is a dry run, so no changes will be written.')); + } + if ($all_repositories || $repositories) { $rebuilt_anything = true; if ($repositories) { $repository_list = $this->loadRepositories($args, 'repository'); } else { $repository_query = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer); $repository_list = new PhabricatorQueryIterator($repository_query); } foreach ($repository_list as $repository) { $commit_query = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->needCommitData(true) ->withRepositoryIDs(array($repository->getID())); // See T13457. Adjust ordering to hit keys better and tweak page size // to improve performance slightly, since these records are small. $commit_query->setOrderVector(array('-epoch', '-id')); $commit_iterator = id(new PhabricatorQueryIterator($commit_query)) ->setPageSize(1000); $this->rebuildCommits($commit_iterator); } } $commits = $args->getArg('commit'); if ($commits) { $rebuilt_anything = true; $commit_list = $this->loadCommits($args, 'commit'); // Reload commits to get commit data. $commit_list = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->needCommitData(true) ->withIDs(mpull($commit_list, 'getID')) ->execute(); $this->rebuildCommits($commit_list); } $users = $args->getArg('user'); if ($users) { $rebuilt_anything = true; $user_list = $this->loadUsersFromArguments($users); $this->rebuildUsers($user_list); } $emails = $args->getArg('email'); if ($emails) { $rebuilt_anything = true; $this->rebuildEmails($emails); } if ($all_identities || $raw) { $rebuilt_anything = true; if ($raw) { $identities = id(new PhabricatorRepositoryIdentityQuery()) ->setViewer($viewer) ->withIdentityNames($raw) ->execute(); $identities = mpull($identities, null, 'getIdentityNameRaw'); foreach ($raw as $raw_identity) { if (!isset($identities[$raw_identity])) { throw new PhutilArgumentUsageException( pht( 'No identity "%s" exists. When selecting identities with '. '"--raw", the entire identity must match exactly.', $raw_identity)); } } $identity_list = $identities; } else { $identity_query = id(new PhabricatorRepositoryIdentityQuery()) ->setViewer($viewer); $identity_list = new PhabricatorQueryIterator($identity_query); $this->logInfo( pht('REBUILD'), pht('Rebuilding all existing identities.')); } $this->rebuildIdentities($identity_list); } if (!$rebuilt_anything) { throw new PhutilArgumentUsageException( pht( 'Nothing specified to rebuild. Use flags to choose which '. 'identities to rebuild, or "--help" for help.')); } return 0; } private function rebuildCommits($commits) { foreach ($commits as $commit) { $needs_update = false; $data = $commit->getCommitData(); $author_name = $data->getAuthorName(); $author_identity = $this->getIdentityForCommit( $commit, $author_name); $author_phid = $commit->getAuthorIdentityPHID(); $identity_phid = $author_identity->getPHID(); $aidentity_phid = $identity_phid; if ($author_phid !== $identity_phid) { $commit->setAuthorIdentityPHID($identity_phid); $data->setCommitDetail('authorIdentityPHID', $identity_phid); $needs_update = true; } $committer_name = $data->getCommitDetail('committer', null); $committer_phid = $commit->getCommitterIdentityPHID(); if (strlen($committer_name)) { $committer_identity = $this->getIdentityForCommit( $commit, $committer_name); $identity_phid = $committer_identity->getPHID(); } else { $identity_phid = null; } if ($committer_phid !== $identity_phid) { $commit->setCommitterIdentityPHID($identity_phid); $data->setCommitDetail('committerIdentityPHID', $identity_phid); $needs_update = true; } if ($needs_update) { $commit->save(); $data->save(); $this->logInfo( pht('COMMIT'), pht( 'Rebuilt identities for "%s".', $commit->getDisplayName())); } else { $this->logInfo( pht('SKIP'), pht( 'No changes for commit "%s".', $commit->getDisplayName())); } } } private function getIdentityForCommit( PhabricatorRepositoryCommit $commit, $raw_identity) { if (!isset($this->identityCache[$raw_identity])) { - $viewer = $this->getViewer(); - - $identity = id(new DiffusionRepositoryIdentityEngine()) - ->setViewer($viewer) + $identity = $this->newIdentityEngine() ->setSourcePHID($commit->getPHID()) ->newResolvedIdentity($raw_identity); $this->identityCache[$raw_identity] = $identity; } return $this->identityCache[$raw_identity]; } private function rebuildUsers($users) { $viewer = $this->getViewer(); foreach ($users as $user) { $this->logInfo( pht('USER'), pht( 'Rebuilding identities for user "%s".', $user->getMonogram())); $emails = id(new PhabricatorUserEmail())->loadAllWhere( 'userPHID = %s', $user->getPHID()); if ($emails) { $this->rebuildEmails(mpull($emails, 'getAddress')); } $identities = id(new PhabricatorRepositoryIdentityQuery()) ->setViewer($viewer) ->withRelatedPHIDs(array($user->getPHID())) ->execute(); if (!$identities) { $this->logWarn( pht('NO IDENTITIES'), pht('Found no identities directly related to user.')); continue; } $this->rebuildIdentities($identities); } } private function rebuildEmails($emails) { $viewer = $this->getViewer(); foreach ($emails as $email) { $this->logInfo( pht('EMAIL'), pht('Rebuilding identities for email address "%s".', $email)); $identities = id(new PhabricatorRepositoryIdentityQuery()) ->setViewer($viewer) ->withEmailAddresses(array($email)) ->execute(); if (!$identities) { $this->logWarn( pht('NO IDENTITIES'), pht('Found no identities for email address "%s".', $email)); continue; } $this->rebuildIdentities($identities); } } private function rebuildIdentities($identities) { - $viewer = $this->getViewer(); + $dry_run = $this->dryRun; foreach ($identities as $identity) { $raw_identity = $identity->getIdentityName(); if (isset($this->identityCache[$raw_identity])) { $this->logInfo( pht('SKIP'), pht( 'Identity "%s" has already been rebuilt.', $raw_identity)); continue; } $this->logInfo( pht('IDENTITY'), pht( 'Rebuilding identity "%s".', $raw_identity)); $old_auto = $identity->getAutomaticGuessedUserPHID(); $old_assign = $identity->getManuallySetUserPHID(); - $identity = id(new DiffusionRepositoryIdentityEngine()) - ->setViewer($viewer) + $identity = $this->newIdentityEngine() ->newUpdatedIdentity($identity); $this->identityCache[$raw_identity] = $identity; $new_auto = $identity->getAutomaticGuessedUserPHID(); $new_assign = $identity->getManuallySetUserPHID(); $same_auto = ($old_auto === $new_auto); $same_assign = ($old_assign === $new_assign); if ($same_auto && $same_assign) { $this->logInfo( pht('UNCHANGED'), pht('No changes to identity.')); } else { if (!$same_auto) { - $this->logWarn( - pht('AUTOMATIC PHID'), - pht( - 'Automatic user updated from "%s" to "%s".', - $this->renderPHID($old_auto), - $this->renderPHID($new_auto))); + if ($dry_run) { + $this->logWarn( + pht('DETECTED PHID'), + pht( + '(Dry Run) Would update detected user from "%s" to "%s".', + $this->renderPHID($old_auto), + $this->renderPHID($new_auto))); + } else { + $this->logWarn( + pht('DETECTED PHID'), + pht( + 'Detected user updated from "%s" to "%s".', + $this->renderPHID($old_auto), + $this->renderPHID($new_auto))); + } } if (!$same_assign) { - $this->logWarn( - pht('ASSIGNED PHID'), - pht( - 'Assigned user updated from "%s" to "%s".', - $this->renderPHID($old_assign), - $this->renderPHID($new_assign))); + if ($dry_run) { + $this->logWarn( + pht('ASSIGNED PHID'), + pht( + '(Dry Run) Would update assigned user from "%s" to "%s".', + $this->renderPHID($old_assign), + $this->renderPHID($new_assign))); + } else { + $this->logWarn( + pht('ASSIGNED PHID'), + pht( + 'Assigned user updated from "%s" to "%s".', + $this->renderPHID($old_assign), + $this->renderPHID($new_assign))); + } } } } } private function renderPHID($phid) { if ($phid == null) { return pht('NULL'); - } else { - return $phid; } + + if (!isset($this->phidCache[$phid])) { + $viewer = $this->getViewer(); + $handles = $viewer->loadHandles(array($phid)); + $this->phidCache[$phid] = pht( + '%s <%s>', + $handles[$phid]->getFullName(), + $phid); + } + + return $this->phidCache[$phid]; + } + + private function newIdentityEngine() { + $viewer = $this->getViewer(); + + return id(new DiffusionRepositoryIdentityEngine()) + ->setViewer($viewer) + ->setDryRun($this->dryRun); } }