Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15459741
D20911.id49833.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D20911.id49833.diff
View Options
diff --git a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
--- a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
+++ b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
@@ -113,7 +113,8 @@
'pre',
array(),
(string)csprintf(
- 'phabricator/ $ ./bin/repository rebuild-identities --all'));
+ 'phabricator/ $ '.
+ './bin/repository rebuild-identities --all-repositories'));
$message[] = pht(
'You can find more information about this new identity mapping '.
diff --git a/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php b/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php
--- a/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php
+++ b/src/applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php
@@ -68,6 +68,24 @@
}
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
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
--- a/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
@@ -14,38 +14,172 @@
->setArguments(
array(
array(
- 'name' => 'repositories',
- 'wildcard' => true,
+ 'name' => 'all-repositories',
+ 'help' => pht('Rebuild identities across all repositories.'),
),
array(
- 'name' => 'all',
- 'help' => pht('Rebuild identities across all repositories.'),
- ),
+ '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.'),
+ ),
));
}
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(
+ 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('Specify --all or a list of repositories, but not both.'));
+ pht(
+ 'Flags "--all-identities" and "--raw" are not '.
+ 'compatible.'));
}
- $query = id(new DiffusionCommitQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->needCommitData(true);
+ if ($all_repositories || $repositories) {
+ $rebuilt_anything = true;
- if ($repositories) {
- $repos = $this->loadRepositories($args, 'repositories');
- $query->withRepositoryIDs(mpull($repos, 'getID'));
+ 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()));
+
+ $commit_iterator = new PhabricatorQueryIterator($commit_query);
+
+ $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.'));
}
- $iterator = new PhabricatorQueryIterator($query);
- foreach ($iterator as $commit) {
+ return 0;
+ }
+
+ private function rebuildCommits($commits) {
+ foreach ($commits as $commit) {
$needs_update = false;
$data = $commit->getCommitData();
@@ -57,6 +191,8 @@
$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);
@@ -83,16 +219,20 @@
if ($needs_update) {
$commit->save();
$data->save();
- echo tsprintf(
- "Rebuilt identities for %s.\n",
- $commit->getDisplayName());
+
+ $this->logInfo(
+ pht('COMMIT'),
+ pht(
+ 'Rebuilt identities for "%s".',
+ $commit->getDisplayName()));
} else {
- echo tsprintf(
- "No changes for %s.\n",
- $commit->getDisplayName());
+ $this->logInfo(
+ pht('SKIP'),
+ pht(
+ 'No changes for commit "%s".',
+ $commit->getDisplayName()));
}
}
-
}
private function getIdentityForCommit(
@@ -113,4 +253,131 @@
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;
+ }
+ }
+
}
diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php
--- a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php
+++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php
@@ -11,6 +11,7 @@
private $effectivePHIDs;
private $identityNameLike;
private $hasEffectivePHID;
+ private $relatedPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -47,6 +48,11 @@
return $this;
}
+ public function withRelatedPHIDs(array $related) {
+ $this->relatedPHIDs = $related;
+ return $this;
+ }
+
public function withHasEffectivePHID($has_effective_phid) {
$this->hasEffectivePHID = $has_effective_phid;
return $this;
@@ -57,7 +63,7 @@
}
protected function getPrimaryTableAlias() {
- return 'repository_identity';
+ return 'identity';
}
protected function loadPage() {
@@ -70,28 +76,28 @@
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.id IN (%Ld)',
+ 'identity.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.phid IN (%Ls)',
+ 'identity.phid IN (%Ls)',
$this->phids);
}
if ($this->assignedPHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.manuallySetUserPHID IN (%Ls)',
+ 'identity.manuallySetUserPHID IN (%Ls)',
$this->assignedPHIDs);
}
if ($this->effectivePHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.currentEffectiveUserPHID IN (%Ls)',
+ 'identity.currentEffectiveUserPHID IN (%Ls)',
$this->effectivePHIDs);
}
@@ -99,11 +105,11 @@
if ($this->hasEffectivePHID) {
$where[] = qsprintf(
$conn,
- 'repository_identity.currentEffectiveUserPHID IS NOT NULL');
+ 'identity.currentEffectiveUserPHID IS NOT NULL');
} else {
$where[] = qsprintf(
$conn,
- 'repository_identity.currentEffectiveUserPHID IS NULL');
+ 'identity.currentEffectiveUserPHID IS NULL');
}
}
@@ -115,24 +121,35 @@
$where[] = qsprintf(
$conn,
- 'repository_identity.identityNameHash IN (%Ls)',
+ 'identity.identityNameHash IN (%Ls)',
$name_hashes);
}
if ($this->emailAddresses !== null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.emailAddress IN (%Ls)',
+ 'identity.emailAddress IN (%Ls)',
$this->emailAddresses);
}
if ($this->identityNameLike != null) {
$where[] = qsprintf(
$conn,
- 'repository_identity.identityNameRaw LIKE %~',
+ 'identity.identityNameRaw LIKE %~',
$this->identityNameLike);
}
+ if ($this->relatedPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ '(identity.manuallySetUserPHID IN (%Ls) OR
+ identity.currentEffectiveUserPHID IN (%Ls) OR
+ identity.automaticGuessedUserPHID IN (%Ls))',
+ $this->relatedPHIDs,
+ $this->relatedPHIDs,
+ $this->relatedPHIDs);
+ }
+
return $where;
}
diff --git a/src/infrastructure/management/PhabricatorManagementWorkflow.php b/src/infrastructure/management/PhabricatorManagementWorkflow.php
--- a/src/infrastructure/management/PhabricatorManagementWorkflow.php
+++ b/src/infrastructure/management/PhabricatorManagementWorkflow.php
@@ -67,4 +67,125 @@
fprintf(STDERR, '%s', $message);
}
+ final protected function loadUsersFromArguments(array $identifiers) {
+ if (!$identifiers) {
+ return array();
+ }
+
+ $ids = array();
+ $phids = array();
+ $usernames = array();
+
+ $user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
+
+ foreach ($identifiers as $identifier) {
+ // If the value is a user PHID, treat as a PHID.
+ if (phid_get_type($identifier) === $user_type) {
+ $phids[$identifier] = $identifier;
+ continue;
+ }
+
+ // If the value is "@..." and then some text, treat it as a username.
+ if ((strlen($identifier) > 1) && ($identifier[0] == '@')) {
+ $usernames[$identifier] = substr($identifier, 1);
+ continue;
+ }
+
+ // If the value is digits, treat it as both an ID and a username.
+ // Entirely numeric usernames, like "1234", are valid.
+ if (ctype_digit($identifier)) {
+ $ids[$identifier] = $identifier;
+ $usernames[$identifier] = $identifier;
+ continue;
+ }
+
+ // Otherwise, treat it as an unescaped username.
+ $usernames[$identifier] = $identifier;
+ }
+
+ $viewer = $this->getViewer();
+ $results = array();
+
+ if ($phids) {
+ $users = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($phids)
+ ->execute();
+ foreach ($users as $user) {
+ $phid = $user->getPHID();
+ $results[$phid][] = $user;
+ }
+ }
+
+ if ($usernames) {
+ $users = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withUsernames($usernames)
+ ->execute();
+
+ $reverse_map = array();
+ foreach ($usernames as $identifier => $username) {
+ $username = phutil_utf8_strtolower($username);
+ $reverse_map[$username][] = $identifier;
+ }
+
+ foreach ($users as $user) {
+ $username = $user->getUsername();
+ $username = phutil_utf8_strtolower($username);
+
+ $reverse_identifiers = idx($reverse_map, $username, array());
+
+ if (count($reverse_identifiers) > 1) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Multiple user identifiers (%s) correspond to the same user. '.
+ 'Identify each user exactly once.',
+ implode(', ', $reverse_identifiers)));
+ }
+
+ foreach ($reverse_identifiers as $reverse_identifier) {
+ $results[$reverse_identifier][] = $user;
+ }
+ }
+ }
+
+ if ($ids) {
+ $users = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
+ foreach ($users as $user) {
+ $id = $user->getID();
+ $results[$id][] = $user;
+ }
+ }
+
+ $list = array();
+ foreach ($identifiers as $identifier) {
+ $users = idx($results, $identifier, array());
+ if (!$users) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'No user "%s" exists. Specify users by username, ID, or PHID.',
+ $identifier));
+ }
+
+ if (count($users) > 1) {
+ // This can happen if you have a user "@25", a user with ID 25, and
+ // specify "--user 25". You can disambiguate this by specifying
+ // "--user @25".
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Identifier "%s" matches multiple users. Specify each user '.
+ 'unambiguously with "@username" or by using user PHIDs.',
+ $identifier));
+ }
+
+ $list[] = head($users);
+ }
+
+ return $list;
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 1, 3:42 PM (1 w, 14 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7435450
Default Alt Text
D20911.id49833.diff (19 KB)
Attached To
Mode
D20911: Add additional flags to "bin/repository rebuild-identities" to improve flexibility
Attached
Detach File
Event Timeline
Log In to Comment