@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 433 lines 12 kB view raw
1<?php 2 3final class PhabricatorRepositoryManagementRebuildIdentitiesWorkflow 4 extends PhabricatorRepositoryManagementWorkflow { 5 6 private $identityCache = array(); 7 private $phidCache = array(); 8 private $dryRun; 9 10 protected function didConstruct() { 11 $this 12 ->setName('rebuild-identities') 13 ->setExamples( 14 '**rebuild-identities** [__options__] __repository__') 15 ->setSynopsis(pht('Rebuild repository identities from commits.')) 16 ->setArguments( 17 array( 18 array( 19 'name' => 'all-repositories', 20 'help' => pht('Rebuild identities across all repositories.'), 21 ), 22 array( 23 'name' => 'all-identities', 24 'help' => pht('Rebuild all currently-known identities.'), 25 ), 26 array( 27 'name' => 'repository', 28 'param' => 'repository', 29 'repeat' => true, 30 'help' => pht('Rebuild identities in a repository.'), 31 ), 32 array( 33 'name' => 'commit', 34 'param' => 'commit', 35 'repeat' => true, 36 'help' => pht('Rebuild identities for a commit.'), 37 ), 38 array( 39 'name' => 'user', 40 'param' => 'user', 41 'repeat' => true, 42 'help' => pht('Rebuild identities for a user.'), 43 ), 44 array( 45 'name' => 'email', 46 'param' => 'email', 47 'repeat' => true, 48 'help' => pht('Rebuild identities for an email address.'), 49 ), 50 array( 51 'name' => 'raw', 52 'param' => 'raw', 53 'repeat' => true, 54 'help' => pht('Rebuild identities for a raw commit string.'), 55 ), 56 array( 57 'name' => 'dry-run', 58 'help' => pht('Show changes, but do not make any changes.'), 59 ), 60 )); 61 } 62 63 public function execute(PhutilArgumentParser $args) { 64 $viewer = $this->getViewer(); 65 66 $rebuilt_anything = false; 67 68 $all_repositories = $args->getArg('all-repositories'); 69 $repositories = $args->getArg('repository'); 70 71 if ($all_repositories && $repositories) { 72 throw new PhutilArgumentUsageException( 73 pht( 74 'Flags "--all-repositories" and "--repository" are not '. 75 'compatible.')); 76 } 77 78 79 $all_identities = $args->getArg('all-identities'); 80 $raw = $args->getArg('raw'); 81 82 if ($all_identities && $raw) { 83 throw new PhutilArgumentUsageException( 84 pht( 85 'Flags "--all-identities" and "--raw" are not '. 86 'compatible.')); 87 } 88 89 $dry_run = $args->getArg('dry-run'); 90 $this->dryRun = $dry_run; 91 92 if ($this->dryRun) { 93 $this->logWarn( 94 pht('DRY RUN'), 95 pht('This is a dry run, so no changes will be written.')); 96 } 97 98 if ($all_repositories || $repositories) { 99 $rebuilt_anything = true; 100 101 if ($repositories) { 102 $repository_list = $this->loadRepositories($args, 'repository'); 103 } else { 104 $repository_query = id(new PhabricatorRepositoryQuery()) 105 ->setViewer($viewer); 106 $repository_list = new PhabricatorQueryIterator($repository_query); 107 } 108 109 foreach ($repository_list as $repository) { 110 $commit_query = id(new DiffusionCommitQuery()) 111 ->setViewer($viewer) 112 ->needCommitData(true) 113 ->withRepositoryIDs(array($repository->getID())); 114 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')); 118 119 $commit_iterator = id(new PhabricatorQueryIterator($commit_query)) 120 ->setPageSize(1000); 121 122 $this->rebuildCommits($commit_iterator); 123 } 124 } 125 126 $commits = $args->getArg('commit'); 127 if ($commits) { 128 $rebuilt_anything = true; 129 $commit_list = $this->loadCommits($args, 'commit'); 130 131 // Reload commits to get commit data. 132 $commit_list = id(new DiffusionCommitQuery()) 133 ->setViewer($viewer) 134 ->needCommitData(true) 135 ->withIDs(mpull($commit_list, 'getID')) 136 ->execute(); 137 138 $this->rebuildCommits($commit_list); 139 } 140 141 $users = $args->getArg('user'); 142 if ($users) { 143 $rebuilt_anything = true; 144 145 $user_list = $this->loadUsersFromArguments($users); 146 $this->rebuildUsers($user_list); 147 } 148 149 $emails = $args->getArg('email'); 150 if ($emails) { 151 $rebuilt_anything = true; 152 $this->rebuildEmails($emails); 153 } 154 155 if ($all_identities || $raw) { 156 $rebuilt_anything = true; 157 158 if ($raw) { 159 $identities = id(new PhabricatorRepositoryIdentityQuery()) 160 ->setViewer($viewer) 161 ->withIdentityNames($raw) 162 ->execute(); 163 164 $identities = mpull($identities, null, 'getIdentityNameRaw'); 165 foreach ($raw as $raw_identity) { 166 if (!isset($identities[$raw_identity])) { 167 throw new PhutilArgumentUsageException( 168 pht( 169 'No identity "%s" exists. When selecting identities with '. 170 '"--raw", the entire identity must match exactly.', 171 $raw_identity)); 172 } 173 } 174 175 $identity_list = $identities; 176 } else { 177 $identity_query = id(new PhabricatorRepositoryIdentityQuery()) 178 ->setViewer($viewer); 179 180 $identity_list = new PhabricatorQueryIterator($identity_query); 181 182 $this->logInfo( 183 pht('REBUILD'), 184 pht('Rebuilding all existing identities.')); 185 } 186 187 $this->rebuildIdentities($identity_list); 188 } 189 190 if (!$rebuilt_anything) { 191 throw new PhutilArgumentUsageException( 192 pht( 193 'Nothing specified to rebuild. Use flags to choose which '. 194 'identities to rebuild, or "--help" for help.')); 195 } 196 197 return 0; 198 } 199 200 private function rebuildCommits($commits) { 201 foreach ($commits as $commit) { 202 $needs_update = false; 203 204 $data = $commit->getCommitData(); 205 $author = $data->getAuthorString(); 206 207 $author_identity = $this->getIdentityForCommit( 208 $commit, 209 $author); 210 211 $author_phid = $commit->getAuthorIdentityPHID(); 212 $identity_phid = $author_identity->getPHID(); 213 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; 219 } 220 221 $committer_name = $data->getCommitterString(); 222 $committer_phid = $commit->getCommitterIdentityPHID(); 223 if (strlen($committer_name)) { 224 $committer_identity = $this->getIdentityForCommit( 225 $commit, 226 $committer_name); 227 $identity_phid = $committer_identity->getPHID(); 228 } else { 229 $identity_phid = null; 230 } 231 232 if ($committer_phid !== $identity_phid) { 233 $commit->setCommitterIdentityPHID($identity_phid); 234 $data->setCommitDetail('committerIdentityPHID', $identity_phid); 235 $needs_update = true; 236 } 237 238 if ($needs_update) { 239 $commit->save(); 240 $data->save(); 241 242 $this->logInfo( 243 pht('COMMIT'), 244 pht( 245 'Rebuilt identities for "%s".', 246 $commit->getDisplayName())); 247 } else { 248 $this->logInfo( 249 pht('SKIP'), 250 pht( 251 'No changes for commit "%s".', 252 $commit->getDisplayName())); 253 } 254 } 255 } 256 257 private function getIdentityForCommit( 258 PhabricatorRepositoryCommit $commit, 259 $raw_identity) { 260 261 if (!isset($this->identityCache[$raw_identity])) { 262 $identity = $this->newIdentityEngine() 263 ->setSourcePHID($commit->getPHID()) 264 ->newResolvedIdentity($raw_identity); 265 266 $this->identityCache[$raw_identity] = $identity; 267 } 268 269 return $this->identityCache[$raw_identity]; 270 } 271 272 273 private function rebuildUsers($users) { 274 $viewer = $this->getViewer(); 275 276 foreach ($users as $user) { 277 $this->logInfo( 278 pht('USER'), 279 pht( 280 'Rebuilding identities for user "%s".', 281 $user->getMonogram())); 282 283 $emails = id(new PhabricatorUserEmail())->loadAllWhere( 284 'userPHID = %s AND isVerified = 1', 285 $user->getPHID()); 286 if ($emails) { 287 $this->rebuildEmails(mpull($emails, 'getAddress')); 288 } 289 290 $identities = id(new PhabricatorRepositoryIdentityQuery()) 291 ->setViewer($viewer) 292 ->withRelatedPHIDs(array($user->getPHID())) 293 ->execute(); 294 295 if (!$identities) { 296 $this->logWarn( 297 pht('NO IDENTITIES'), 298 pht('Found no identities directly related to user.')); 299 continue; 300 } 301 302 $this->rebuildIdentities($identities); 303 } 304 } 305 306 private function rebuildEmails($emails) { 307 $viewer = $this->getViewer(); 308 309 foreach ($emails as $email) { 310 $this->logInfo( 311 pht('EMAIL'), 312 pht('Rebuilding identities for email address "%s".', $email)); 313 314 $identities = id(new PhabricatorRepositoryIdentityQuery()) 315 ->setViewer($viewer) 316 ->withEmailAddresses(array($email)) 317 ->execute(); 318 319 if (!$identities) { 320 $this->logWarn( 321 pht('NO IDENTITIES'), 322 pht('Found no identities for email address "%s".', $email)); 323 continue; 324 } 325 326 $this->rebuildIdentities($identities); 327 } 328 } 329 330 private function rebuildIdentities($identities) { 331 $dry_run = $this->dryRun; 332 333 foreach ($identities as $identity) { 334 $raw_identity = $identity->getIdentityName(); 335 336 if (isset($this->identityCache[$raw_identity])) { 337 $this->logInfo( 338 pht('SKIP'), 339 pht( 340 'Identity "%s" has already been rebuilt.', 341 $raw_identity)); 342 continue; 343 } 344 345 $this->logInfo( 346 pht('IDENTITY'), 347 pht( 348 'Rebuilding identity "%s".', 349 $raw_identity)); 350 351 $old_auto = $identity->getAutomaticGuessedUserPHID(); 352 $old_assign = $identity->getManuallySetUserPHID(); 353 354 $identity = $this->newIdentityEngine() 355 ->newUpdatedIdentity($identity); 356 357 $this->identityCache[$raw_identity] = $identity; 358 359 $new_auto = $identity->getAutomaticGuessedUserPHID(); 360 $new_assign = $identity->getManuallySetUserPHID(); 361 362 $same_auto = ($old_auto === $new_auto); 363 $same_assign = ($old_assign === $new_assign); 364 365 if ($same_auto && $same_assign) { 366 $this->logInfo( 367 pht('UNCHANGED'), 368 pht('No changes to identity.')); 369 } else { 370 if (!$same_auto) { 371 if ($dry_run) { 372 $this->logWarn( 373 pht('DETECTED PHID'), 374 pht( 375 '(Dry Run) Would update detected user from "%s" to "%s".', 376 $this->renderPHID($old_auto), 377 $this->renderPHID($new_auto))); 378 } else { 379 $this->logWarn( 380 pht('DETECTED PHID'), 381 pht( 382 'Detected user updated from "%s" to "%s".', 383 $this->renderPHID($old_auto), 384 $this->renderPHID($new_auto))); 385 } 386 } 387 if (!$same_assign) { 388 if ($dry_run) { 389 $this->logWarn( 390 pht('ASSIGNED PHID'), 391 pht( 392 '(Dry Run) Would update assigned user from "%s" to "%s".', 393 $this->renderPHID($old_assign), 394 $this->renderPHID($new_assign))); 395 } else { 396 $this->logWarn( 397 pht('ASSIGNED PHID'), 398 pht( 399 'Assigned user updated from "%s" to "%s".', 400 $this->renderPHID($old_assign), 401 $this->renderPHID($new_assign))); 402 } 403 } 404 } 405 } 406 } 407 408 private function renderPHID($phid) { 409 if ($phid == null) { 410 return pht('NULL'); 411 } 412 413 if (!isset($this->phidCache[$phid])) { 414 $viewer = $this->getViewer(); 415 $handles = $viewer->loadHandles(array($phid)); 416 $this->phidCache[$phid] = pht( 417 '%s <%s>', 418 $handles[$phid]->getFullName(), 419 $phid); 420 } 421 422 return $this->phidCache[$phid]; 423 } 424 425 private function newIdentityEngine() { 426 $viewer = $this->getViewer(); 427 428 return id(new DiffusionRepositoryIdentityEngine()) 429 ->setViewer($viewer) 430 ->setDryRun($this->dryRun); 431 } 432 433}