Differential D20911 Diff 49833 src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
<?php | <?php | ||||
final class PhabricatorRepositoryManagementRebuildIdentitiesWorkflow | final class PhabricatorRepositoryManagementRebuildIdentitiesWorkflow | ||||
extends PhabricatorRepositoryManagementWorkflow { | extends PhabricatorRepositoryManagementWorkflow { | ||||
private $identityCache = array(); | private $identityCache = array(); | ||||
protected function didConstruct() { | protected function didConstruct() { | ||||
$this | $this | ||||
->setName('rebuild-identities') | ->setName('rebuild-identities') | ||||
->setExamples( | ->setExamples( | ||||
'**rebuild-identities** [__options__] __repository__') | '**rebuild-identities** [__options__] __repository__') | ||||
->setSynopsis(pht('Rebuild repository identities from commits.')) | ->setSynopsis(pht('Rebuild repository identities from commits.')) | ||||
->setArguments( | ->setArguments( | ||||
array( | array( | ||||
array( | array( | ||||
'name' => 'repositories', | 'name' => 'all-repositories', | ||||
'wildcard' => true, | 'help' => pht('Rebuild identities across all repositories.'), | ||||
), | ), | ||||
array( | array( | ||||
'name' => 'all', | 'name' => 'all-identities', | ||||
'help' => pht('Rebuild identities across all repositories.'), | '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.'), | |||||
), | ), | ||||
)); | )); | ||||
} | } | ||||
public function execute(PhutilArgumentParser $args) { | public function execute(PhutilArgumentParser $args) { | ||||
$console = PhutilConsole::getConsole(); | $viewer = $this->getViewer(); | ||||
$rebuilt_anything = false; | |||||
$all = $args->getArg('all'); | |||||
$repositories = $args->getArg('repositories'); | |||||
if ($all xor empty($repositories)) { | $all_repositories = $args->getArg('all-repositories'); | ||||
$repositories = $args->getArg('repository'); | |||||
if ($all_repositories && $repositories) { | |||||
throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
pht('Specify --all or a list of repositories, but not both.')); | pht( | ||||
'Flags "--all-repositories" and "--repository" are not '. | |||||
'compatible.')); | |||||
} | } | ||||
$query = id(new DiffusionCommitQuery()) | |||||
->setViewer(PhabricatorUser::getOmnipotentUser()) | $all_identities = $args->getArg('all-identities'); | ||||
->needCommitData(true); | $raw = $args->getArg('raw'); | ||||
if ($all_identities && $raw) { | |||||
throw new PhutilArgumentUsageException( | |||||
pht( | |||||
'Flags "--all-identities" and "--raw" are not '. | |||||
'compatible.')); | |||||
} | |||||
if ($all_repositories || $repositories) { | |||||
$rebuilt_anything = true; | |||||
if ($repositories) { | if ($repositories) { | ||||
$repos = $this->loadRepositories($args, 'repositories'); | $repository_list = $this->loadRepositories($args, 'repository'); | ||||
$query->withRepositoryIDs(mpull($repos, 'getID')); | } 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())); | |||||
$commit_iterator = new PhabricatorQueryIterator($commit_query); | |||||
$this->rebuildCommits($commit_iterator); | |||||
} | |||||
} | } | ||||
$iterator = new PhabricatorQueryIterator($query); | $commits = $args->getArg('commit'); | ||||
foreach ($iterator as $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; | $needs_update = false; | ||||
$data = $commit->getCommitData(); | $data = $commit->getCommitData(); | ||||
$author_name = $data->getAuthorName(); | $author_name = $data->getAuthorName(); | ||||
$author_identity = $this->getIdentityForCommit( | $author_identity = $this->getIdentityForCommit( | ||||
$commit, | $commit, | ||||
$author_name); | $author_name); | ||||
$author_phid = $commit->getAuthorIdentityPHID(); | $author_phid = $commit->getAuthorIdentityPHID(); | ||||
$identity_phid = $author_identity->getPHID(); | $identity_phid = $author_identity->getPHID(); | ||||
$aidentity_phid = $identity_phid; | |||||
if ($author_phid !== $identity_phid) { | if ($author_phid !== $identity_phid) { | ||||
$commit->setAuthorIdentityPHID($identity_phid); | $commit->setAuthorIdentityPHID($identity_phid); | ||||
$data->setCommitDetail('authorIdentityPHID', $identity_phid); | $data->setCommitDetail('authorIdentityPHID', $identity_phid); | ||||
$needs_update = true; | $needs_update = true; | ||||
} | } | ||||
$committer_name = $data->getCommitDetail('committer', null); | $committer_name = $data->getCommitDetail('committer', null); | ||||
$committer_phid = $commit->getCommitterIdentityPHID(); | $committer_phid = $commit->getCommitterIdentityPHID(); | ||||
Show All 10 Lines | foreach ($commits as $commit) { | ||||
$commit->setCommitterIdentityPHID($identity_phid); | $commit->setCommitterIdentityPHID($identity_phid); | ||||
$data->setCommitDetail('committerIdentityPHID', $identity_phid); | $data->setCommitDetail('committerIdentityPHID', $identity_phid); | ||||
$needs_update = true; | $needs_update = true; | ||||
} | } | ||||
if ($needs_update) { | if ($needs_update) { | ||||
$commit->save(); | $commit->save(); | ||||
$data->save(); | $data->save(); | ||||
echo tsprintf( | |||||
"Rebuilt identities for %s.\n", | $this->logInfo( | ||||
$commit->getDisplayName()); | pht('COMMIT'), | ||||
} else { | pht( | ||||
echo tsprintf( | 'Rebuilt identities for "%s".', | ||||
"No changes for %s.\n", | $commit->getDisplayName())); | ||||
$commit->getDisplayName()); | } else { | ||||
$this->logInfo( | |||||
pht('SKIP'), | |||||
pht( | |||||
'No changes for commit "%s".', | |||||
$commit->getDisplayName())); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private function getIdentityForCommit( | private function getIdentityForCommit( | ||||
PhabricatorRepositoryCommit $commit, | PhabricatorRepositoryCommit $commit, | ||||
$raw_identity) { | $raw_identity) { | ||||
if (!isset($this->identityCache[$raw_identity])) { | if (!isset($this->identityCache[$raw_identity])) { | ||||
$viewer = $this->getViewer(); | $viewer = $this->getViewer(); | ||||
$identity = id(new DiffusionRepositoryIdentityEngine()) | $identity = id(new DiffusionRepositoryIdentityEngine()) | ||||
->setViewer($viewer) | ->setViewer($viewer) | ||||
->setSourcePHID($commit->getPHID()) | ->setSourcePHID($commit->getPHID()) | ||||
->newResolvedIdentity($raw_identity); | ->newResolvedIdentity($raw_identity); | ||||
$this->identityCache[$raw_identity] = $identity; | $this->identityCache[$raw_identity] = $identity; | ||||
} | } | ||||
return $this->identityCache[$raw_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(); | |||||
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) | |||||
->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 (!$same_assign) { | |||||
$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; | |||||
} | |||||
} | |||||
} | } |