3 final class PhabricatorRepositoryManagementRebuildIdentitiesWorkflow
4 extends PhabricatorRepositoryManagementWorkflow
{
6 private $identityCache = array();
7 private $phidCache = array();
10 protected function didConstruct() {
12 ->setName('rebuild-identities')
14 '**rebuild-identities** [__options__] __repository__')
15 ->setSynopsis(pht('Rebuild repository identities from commits.'))
19 'name' => 'all-repositories',
20 'help' => pht('Rebuild identities across all repositories.'),
23 'name' => 'all-identities',
24 'help' => pht('Rebuild all currently-known identities.'),
27 'name' => 'repository',
28 'param' => 'repository',
30 'help' => pht('Rebuild identities in a repository.'),
36 'help' => pht('Rebuild identities for a commit.'),
42 'help' => pht('Rebuild identities for a user.'),
48 'help' => pht('Rebuild identities for an email address.'),
54 'help' => pht('Rebuild identities for a raw commit string.'),
58 'help' => pht('Show changes, but do not make any changes.'),
63 public function execute(PhutilArgumentParser
$args) {
64 $viewer = $this->getViewer();
66 $rebuilt_anything = false;
68 $all_repositories = $args->getArg('all-repositories');
69 $repositories = $args->getArg('repository');
71 if ($all_repositories && $repositories) {
72 throw new PhutilArgumentUsageException(
74 'Flags "--all-repositories" and "--repository" are not '.
79 $all_identities = $args->getArg('all-identities');
80 $raw = $args->getArg('raw');
82 if ($all_identities && $raw) {
83 throw new PhutilArgumentUsageException(
85 'Flags "--all-identities" and "--raw" are not '.
89 $dry_run = $args->getArg('dry-run');
90 $this->dryRun
= $dry_run;
95 pht('This is a dry run, so no changes will be written.'));
98 if ($all_repositories ||
$repositories) {
99 $rebuilt_anything = true;
102 $repository_list = $this->loadRepositories($args, 'repository');
104 $repository_query = id(new PhabricatorRepositoryQuery())
105 ->setViewer($viewer);
106 $repository_list = new PhabricatorQueryIterator($repository_query);
109 foreach ($repository_list as $repository) {
110 $commit_query = id(new DiffusionCommitQuery())
112 ->needCommitData(true)
113 ->withRepositoryIDs(array($repository->getID()));
115 // See T13457. Adjust ordering to hit keys better and tweak page size
116 // to improve performance slightly, since these records are small.
117 $commit_query->setOrderVector(array('-epoch', '-id'));
119 $commit_iterator = id(new PhabricatorQueryIterator($commit_query))
122 $this->rebuildCommits($commit_iterator);
126 $commits = $args->getArg('commit');
128 $rebuilt_anything = true;
129 $commit_list = $this->loadCommits($args, 'commit');
131 // Reload commits to get commit data.
132 $commit_list = id(new DiffusionCommitQuery())
134 ->needCommitData(true)
135 ->withIDs(mpull($commit_list, 'getID'))
138 $this->rebuildCommits($commit_list);
141 $users = $args->getArg('user');
143 $rebuilt_anything = true;
145 $user_list = $this->loadUsersFromArguments($users);
146 $this->rebuildUsers($user_list);
149 $emails = $args->getArg('email');
151 $rebuilt_anything = true;
152 $this->rebuildEmails($emails);
155 if ($all_identities ||
$raw) {
156 $rebuilt_anything = true;
159 $identities = id(new PhabricatorRepositoryIdentityQuery())
161 ->withIdentityNames($raw)
164 $identities = mpull($identities, null, 'getIdentityNameRaw');
165 foreach ($raw as $raw_identity) {
166 if (!isset($identities[$raw_identity])) {
167 throw new PhutilArgumentUsageException(
169 'No identity "%s" exists. When selecting identities with '.
170 '"--raw", the entire identity must match exactly.',
175 $identity_list = $identities;
177 $identity_query = id(new PhabricatorRepositoryIdentityQuery())
178 ->setViewer($viewer);
180 $identity_list = new PhabricatorQueryIterator($identity_query);
184 pht('Rebuilding all existing identities.'));
187 $this->rebuildIdentities($identity_list);
190 if (!$rebuilt_anything) {
191 throw new PhutilArgumentUsageException(
193 'Nothing specified to rebuild. Use flags to choose which '.
194 'identities to rebuild, or "--help" for help.'));
200 private function rebuildCommits($commits) {
201 foreach ($commits as $commit) {
202 $needs_update = false;
204 $data = $commit->getCommitData();
205 $author = $data->getAuthorString();
207 $author_identity = $this->getIdentityForCommit(
211 $author_phid = $commit->getAuthorIdentityPHID();
212 $identity_phid = $author_identity->getPHID();
214 $aidentity_phid = $identity_phid;
215 if ($author_phid !== $identity_phid) {
216 $commit->setAuthorIdentityPHID($identity_phid);
217 $data->setCommitDetail('authorIdentityPHID', $identity_phid);
218 $needs_update = true;
221 $committer_name = $data->getCommitterString();
222 $committer_phid = $commit->getCommitterIdentityPHID();
223 if (phutil_nonempty_string($committer_name)) {
224 $committer_identity = $this->getIdentityForCommit(
227 $identity_phid = $committer_identity->getPHID();
229 $identity_phid = null;
232 if ($committer_phid !== $identity_phid) {
233 $commit->setCommitterIdentityPHID($identity_phid);
234 $data->setCommitDetail('committerIdentityPHID', $identity_phid);
235 $needs_update = true;
245 'Rebuilt identities for "%s".',
246 $commit->getDisplayName()));
251 'No changes for commit "%s".',
252 $commit->getDisplayName()));
257 private function getIdentityForCommit(
258 PhabricatorRepositoryCommit
$commit,
261 if (!isset($this->identityCache
[$raw_identity])) {
262 $identity = $this->newIdentityEngine()
263 ->setSourcePHID($commit->getPHID())
264 ->newResolvedIdentity($raw_identity);
266 $this->identityCache
[$raw_identity] = $identity;
269 return $this->identityCache
[$raw_identity];
273 private function rebuildUsers($users) {
274 $viewer = $this->getViewer();
276 foreach ($users as $user) {
280 'Rebuilding identities for user "%s".',
281 $user->getMonogram()));
283 $emails = id(new PhabricatorUserEmail())->loadAllWhere(
287 $this->rebuildEmails(mpull($emails, 'getAddress'));
290 $identities = id(new PhabricatorRepositoryIdentityQuery())
292 ->withRelatedPHIDs(array($user->getPHID()))
297 pht('NO IDENTITIES'),
298 pht('Found no identities directly related to user.'));
302 $this->rebuildIdentities($identities);
306 private function rebuildEmails($emails) {
307 $viewer = $this->getViewer();
309 foreach ($emails as $email) {
312 pht('Rebuilding identities for email address "%s".', $email));
314 $identities = id(new PhabricatorRepositoryIdentityQuery())
316 ->withEmailAddresses(array($email))
321 pht('NO IDENTITIES'),
322 pht('Found no identities for email address "%s".', $email));
326 $this->rebuildIdentities($identities);
330 private function rebuildIdentities($identities) {
331 $dry_run = $this->dryRun
;
333 foreach ($identities as $identity) {
334 $raw_identity = $identity->getIdentityName();
336 if (isset($this->identityCache
[$raw_identity])) {
340 'Identity "%s" has already been rebuilt.',
348 'Rebuilding identity "%s".',
351 $old_auto = $identity->getAutomaticGuessedUserPHID();
352 $old_assign = $identity->getManuallySetUserPHID();
354 $identity = $this->newIdentityEngine()
355 ->newUpdatedIdentity($identity);
357 $this->identityCache
[$raw_identity] = $identity;
359 $new_auto = $identity->getAutomaticGuessedUserPHID();
360 $new_assign = $identity->getManuallySetUserPHID();
362 $same_auto = ($old_auto === $new_auto);
363 $same_assign = ($old_assign === $new_assign);
365 if ($same_auto && $same_assign) {
368 pht('No changes to identity.'));
373 pht('DETECTED PHID'),
375 '(Dry Run) Would update detected user from "%s" to "%s".',
376 $this->renderPHID($old_auto),
377 $this->renderPHID($new_auto)));
380 pht('DETECTED PHID'),
382 'Detected user updated from "%s" to "%s".',
383 $this->renderPHID($old_auto),
384 $this->renderPHID($new_auto)));
390 pht('ASSIGNED PHID'),
392 '(Dry Run) Would update assigned user from "%s" to "%s".',
393 $this->renderPHID($old_assign),
394 $this->renderPHID($new_assign)));
397 pht('ASSIGNED PHID'),
399 'Assigned user updated from "%s" to "%s".',
400 $this->renderPHID($old_assign),
401 $this->renderPHID($new_assign)));
408 private function renderPHID($phid) {
413 if (!isset($this->phidCache
[$phid])) {
414 $viewer = $this->getViewer();
415 $handles = $viewer->loadHandles(array($phid));
416 $this->phidCache
[$phid] = pht(
418 $handles[$phid]->getFullName(),
422 return $this->phidCache
[$phid];
425 private function newIdentityEngine() {
426 $viewer = $this->getViewer();
428 return id(new DiffusionRepositoryIdentityEngine())
430 ->setDryRun($this->dryRun
);