Path: blob/master/src/applications/diffusion/query/DiffusionCommitQuery.php
12242 views
<?php12final class DiffusionCommitQuery3extends PhabricatorCursorPagedPolicyAwareQuery {45private $ids;6private $phids;7private $authorPHIDs;8private $defaultRepository;9private $identifiers;10private $repositoryIDs;11private $repositoryPHIDs;12private $identifierMap;13private $responsiblePHIDs;14private $statuses;15private $packagePHIDs;16private $unreachable;17private $permanent;1819private $needAuditRequests;20private $needAuditAuthority;21private $auditIDs;22private $auditorPHIDs;23private $epochMin;24private $epochMax;25private $importing;26private $ancestorsOf;2728private $needCommitData;29private $needDrafts;30private $needIdentities;3132private $mustFilterRefs = false;33private $refRepository;3435public function withIDs(array $ids) {36$this->ids = $ids;37return $this;38}3940public function withPHIDs(array $phids) {41$this->phids = $phids;42return $this;43}4445public function withAuthorPHIDs(array $phids) {46$this->authorPHIDs = $phids;47return $this;48}4950/**51* Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234",52* or "a9caf12". When an identifier matches multiple commits, they will all53* be returned; callers should be prepared to deal with more results than54* they queried for.55*/56public function withIdentifiers(array $identifiers) {57// Some workflows (like blame lookups) can pass in large numbers of58// duplicate identifiers. We only care about unique identifiers, so59// get rid of duplicates immediately.60$identifiers = array_fuse($identifiers);6162$this->identifiers = $identifiers;63return $this;64}6566/**67* Look up commits in a specific repository. This is a shorthand for calling68* @{method:withDefaultRepository} and @{method:withRepositoryIDs}.69*/70public function withRepository(PhabricatorRepository $repository) {71$this->withDefaultRepository($repository);72$this->withRepositoryIDs(array($repository->getID()));73return $this;74}7576/**77* Look up commits in a specific repository. Prefer78* @{method:withRepositoryIDs}; the underlying table is keyed by ID such79* that this method requires a separate initial query to map PHID to ID.80*/81public function withRepositoryPHIDs(array $phids) {82$this->repositoryPHIDs = $phids;83return $this;84}8586/**87* If a default repository is provided, ambiguous commit identifiers will88* be assumed to belong to the default repository.89*90* For example, "r123" appearing in a commit message in repository X is91* likely to be unambiguously "rX123". Normally the reference would be92* considered ambiguous, but if you provide a default repository it will93* be correctly resolved.94*/95public function withDefaultRepository(PhabricatorRepository $repository) {96$this->defaultRepository = $repository;97return $this;98}99100public function withRepositoryIDs(array $repository_ids) {101$this->repositoryIDs = array_unique($repository_ids);102return $this;103}104105public function needCommitData($need) {106$this->needCommitData = $need;107return $this;108}109110public function needDrafts($need) {111$this->needDrafts = $need;112return $this;113}114115public function needIdentities($need) {116$this->needIdentities = $need;117return $this;118}119120public function needAuditRequests($need) {121$this->needAuditRequests = $need;122return $this;123}124125public function needAuditAuthority(array $users) {126assert_instances_of($users, 'PhabricatorUser');127$this->needAuditAuthority = $users;128return $this;129}130131public function withAuditIDs(array $ids) {132$this->auditIDs = $ids;133return $this;134}135136public function withAuditorPHIDs(array $auditor_phids) {137$this->auditorPHIDs = $auditor_phids;138return $this;139}140141public function withResponsiblePHIDs(array $responsible_phids) {142$this->responsiblePHIDs = $responsible_phids;143return $this;144}145146public function withPackagePHIDs(array $package_phids) {147$this->packagePHIDs = $package_phids;148return $this;149}150151public function withUnreachable($unreachable) {152$this->unreachable = $unreachable;153return $this;154}155156public function withPermanent($permanent) {157$this->permanent = $permanent;158return $this;159}160161public function withStatuses(array $statuses) {162$this->statuses = $statuses;163return $this;164}165166public function withEpochRange($min, $max) {167$this->epochMin = $min;168$this->epochMax = $max;169return $this;170}171172public function withImporting($importing) {173$this->importing = $importing;174return $this;175}176177public function withAncestorsOf(array $refs) {178$this->ancestorsOf = $refs;179return $this;180}181182public function getIdentifierMap() {183if ($this->identifierMap === null) {184throw new Exception(185pht(186'You must %s the query before accessing the identifier map.',187'execute()'));188}189return $this->identifierMap;190}191192protected function getPrimaryTableAlias() {193return 'commit';194}195196protected function willExecute() {197if ($this->identifierMap === null) {198$this->identifierMap = array();199}200}201202public function newResultObject() {203return new PhabricatorRepositoryCommit();204}205206protected function loadPage() {207$table = $this->newResultObject();208$conn = $table->establishConnection('r');209210$empty_exception = null;211$subqueries = array();212if ($this->responsiblePHIDs) {213$base_authors = $this->authorPHIDs;214$base_auditors = $this->auditorPHIDs;215216$responsible_phids = $this->responsiblePHIDs;217if ($base_authors) {218$all_authors = array_merge($base_authors, $responsible_phids);219} else {220$all_authors = $responsible_phids;221}222223if ($base_auditors) {224$all_auditors = array_merge($base_auditors, $responsible_phids);225} else {226$all_auditors = $responsible_phids;227}228229$this->authorPHIDs = $all_authors;230$this->auditorPHIDs = $base_auditors;231try {232$subqueries[] = $this->buildStandardPageQuery(233$conn,234$table->getTableName());235} catch (PhabricatorEmptyQueryException $ex) {236$empty_exception = $ex;237}238239$this->authorPHIDs = $base_authors;240$this->auditorPHIDs = $all_auditors;241try {242$subqueries[] = $this->buildStandardPageQuery(243$conn,244$table->getTableName());245} catch (PhabricatorEmptyQueryException $ex) {246$empty_exception = $ex;247}248} else {249$subqueries[] = $this->buildStandardPageQuery(250$conn,251$table->getTableName());252}253254if (!$subqueries) {255throw $empty_exception;256}257258if (count($subqueries) > 1) {259$unions = null;260foreach ($subqueries as $subquery) {261if (!$unions) {262$unions = qsprintf(263$conn,264'(%Q)',265$subquery);266continue;267}268269$unions = qsprintf(270$conn,271'%Q UNION DISTINCT (%Q)',272$unions,273$subquery);274}275276$query = qsprintf(277$conn,278'%Q %Q %Q',279$unions,280$this->buildOrderClause($conn, true),281$this->buildLimitClause($conn));282} else {283$query = head($subqueries);284}285286$rows = queryfx_all($conn, '%Q', $query);287$rows = $this->didLoadRawRows($rows);288289return $table->loadAllFromArray($rows);290}291292protected function willFilterPage(array $commits) {293$repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID');294$repos = id(new PhabricatorRepositoryQuery())295->setViewer($this->getViewer())296->withIDs($repository_ids)297->execute();298299$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;300$result = array();301302foreach ($commits as $key => $commit) {303$repo = idx($repos, $commit->getRepositoryID());304if ($repo) {305$commit->attachRepository($repo);306} else {307$this->didRejectResult($commit);308unset($commits[$key]);309continue;310}311312// Build the identifierMap313if ($this->identifiers !== null) {314$ids = $this->identifiers;315$prefixes = array(316'r'.$commit->getRepository()->getCallsign(),317'r'.$commit->getRepository()->getCallsign().':',318'R'.$commit->getRepository()->getID().':',319'', // No prefix is valid too and will only match the commitIdentifier320);321$suffix = $commit->getCommitIdentifier();322323if ($commit->getRepository()->isSVN()) {324foreach ($prefixes as $prefix) {325if (isset($ids[$prefix.$suffix])) {326$result[$prefix.$suffix][] = $commit;327}328}329} else {330// This awkward construction is so we can link the commits up in O(N)331// time instead of O(N^2).332for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) {333$part = substr($suffix, 0, $ii);334foreach ($prefixes as $prefix) {335if (isset($ids[$prefix.$part])) {336$result[$prefix.$part][] = $commit;337}338}339}340}341}342}343344if ($result) {345foreach ($result as $identifier => $matching_commits) {346if (count($matching_commits) == 1) {347$result[$identifier] = head($matching_commits);348} else {349// This reference is ambiguous (it matches more than one commit) so350// don't link it.351unset($result[$identifier]);352}353}354$this->identifierMap += $result;355}356357return $commits;358}359360protected function didFilterPage(array $commits) {361$viewer = $this->getViewer();362363if ($this->mustFilterRefs) {364// If this flag is set, the query has an "Ancestors Of" constraint and365// at least one of the constraining refs had too many ancestors for us366// to apply the constraint with a big "commitIdentifier IN (%Ls)" clause.367// We're going to filter each page and hope we get a full result set368// before the query overheats.369370$ancestor_list = mpull($commits, 'getCommitIdentifier');371$ancestor_list = array_values($ancestor_list);372373foreach ($this->ancestorsOf as $ref) {374try {375$ancestor_list = DiffusionQuery::callConduitWithDiffusionRequest(376$viewer,377DiffusionRequest::newFromDictionary(378array(379'repository' => $this->refRepository,380'user' => $viewer,381)),382'diffusion.internal.ancestors',383array(384'ref' => $ref,385'commits' => $ancestor_list,386));387} catch (ConduitClientException $ex) {388throw new PhabricatorSearchConstraintException(389$ex->getMessage());390}391392if (!$ancestor_list) {393break;394}395}396397$ancestor_list = array_fuse($ancestor_list);398foreach ($commits as $key => $commit) {399$identifier = $commit->getCommitIdentifier();400if (!isset($ancestor_list[$identifier])) {401$this->didRejectResult($commit);402unset($commits[$key]);403}404}405406if (!$commits) {407return $commits;408}409}410411if ($this->needCommitData) {412$data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(413'commitID in (%Ld)',414mpull($commits, 'getID'));415$data = mpull($data, null, 'getCommitID');416foreach ($commits as $commit) {417$commit_data = idx($data, $commit->getID());418if (!$commit_data) {419$commit_data = new PhabricatorRepositoryCommitData();420}421$commit->attachCommitData($commit_data);422}423}424425if ($this->needAuditRequests) {426$requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere(427'commitPHID IN (%Ls)',428mpull($commits, 'getPHID'));429430$requests = mgroup($requests, 'getCommitPHID');431foreach ($commits as $commit) {432$audit_requests = idx($requests, $commit->getPHID(), array());433$commit->attachAudits($audit_requests);434foreach ($audit_requests as $audit_request) {435$audit_request->attachCommit($commit);436}437}438}439440if ($this->needIdentities) {441$identity_phids = array_merge(442mpull($commits, 'getAuthorIdentityPHID'),443mpull($commits, 'getCommitterIdentityPHID'));444445$data = id(new PhabricatorRepositoryIdentityQuery())446->withPHIDs($identity_phids)447->setViewer($this->getViewer())448->execute();449$data = mpull($data, null, 'getPHID');450451foreach ($commits as $commit) {452$author_identity = idx($data, $commit->getAuthorIdentityPHID());453$committer_identity = idx($data, $commit->getCommitterIdentityPHID());454$commit->attachIdentities($author_identity, $committer_identity);455}456}457458if ($this->needDrafts) {459PhabricatorDraftEngine::attachDrafts(460$viewer,461$commits);462}463464if ($this->needAuditAuthority) {465$authority_users = $this->needAuditAuthority;466467// NOTE: This isn't very efficient since we're running two queries per468// user, but there's currently no way to figure out authority for469// multiple users in one query. Today, we only ever request authority for470// a single user and single commit, so this has no practical impact.471472// NOTE: We're querying with the viewership of query viewer, not the473// actual users. If the viewer can't see a project or package, they474// won't be able to see who has authority on it. This is safer than475// showing them true authority, and should never matter today, but it476// also doesn't seem like a significant disclosure and might be477// reasonable to adjust later if it causes something weird or confusing478// to happen.479480$authority_map = array();481foreach ($authority_users as $authority_user) {482$authority_phid = $authority_user->getPHID();483if (!$authority_phid) {484continue;485}486487$result_phids = array();488489// Users have authority over themselves.490$result_phids[] = $authority_phid;491492// Users have authority over packages they own.493$owned_packages = id(new PhabricatorOwnersPackageQuery())494->setViewer($viewer)495->withAuthorityPHIDs(array($authority_phid))496->execute();497foreach ($owned_packages as $package) {498$result_phids[] = $package->getPHID();499}500501// Users have authority over projects they're members of.502$projects = id(new PhabricatorProjectQuery())503->setViewer($viewer)504->withMemberPHIDs(array($authority_phid))505->execute();506foreach ($projects as $project) {507$result_phids[] = $project->getPHID();508}509510$result_phids = array_fuse($result_phids);511512foreach ($commits as $commit) {513$attach_phids = $result_phids;514515// NOTE: When modifying your own commits, you act only on behalf of516// yourself, not your packages or projects. The idea here is that you517// can't accept your own commits. In the future, this might change or518// depend on configuration.519$author_phid = $commit->getAuthorPHID();520if ($author_phid == $authority_phid) {521$attach_phids = array($author_phid);522$attach_phids = array_fuse($attach_phids);523}524525$commit->attachAuditAuthority($authority_user, $attach_phids);526}527}528}529530return $commits;531}532533protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {534$where = parent::buildWhereClauseParts($conn);535536if ($this->repositoryPHIDs !== null) {537$map_repositories = id(new PhabricatorRepositoryQuery())538->setViewer($this->getViewer())539->withPHIDs($this->repositoryPHIDs)540->execute();541542if (!$map_repositories) {543throw new PhabricatorEmptyQueryException();544}545$repository_ids = mpull($map_repositories, 'getID');546if ($this->repositoryIDs !== null) {547$repository_ids = array_merge($repository_ids, $this->repositoryIDs);548}549$this->withRepositoryIDs($repository_ids);550}551552if ($this->ancestorsOf !== null) {553if (count($this->repositoryIDs) !== 1) {554throw new PhabricatorSearchConstraintException(555pht(556'To search for commits which are ancestors of particular refs, '.557'you must constrain the search to exactly one repository.'));558}559560$repository_id = head($this->repositoryIDs);561$history_limit = $this->getRawResultLimit() * 32;562$viewer = $this->getViewer();563564$repository = id(new PhabricatorRepositoryQuery())565->setViewer($viewer)566->withIDs(array($repository_id))567->executeOne();568569if (!$repository) {570throw new PhabricatorEmptyQueryException();571}572573if ($repository->isSVN()) {574throw new PhabricatorSearchConstraintException(575pht(576'Subversion does not support searching for ancestors of '.577'a particular ref. This operation is not meaningful in '.578'Subversion.'));579}580581if ($repository->isHg()) {582throw new PhabricatorSearchConstraintException(583pht(584'Mercurial does not currently support searching for ancestors of '.585'a particular ref.'));586}587588$can_constrain = true;589$history_identifiers = array();590foreach ($this->ancestorsOf as $key => $ref) {591try {592$raw_history = DiffusionQuery::callConduitWithDiffusionRequest(593$viewer,594DiffusionRequest::newFromDictionary(595array(596'repository' => $repository,597'user' => $viewer,598)),599'diffusion.historyquery',600array(601'commit' => $ref,602'limit' => $history_limit,603));604} catch (ConduitClientException $ex) {605throw new PhabricatorSearchConstraintException(606$ex->getMessage());607}608609$ref_identifiers = array();610foreach ($raw_history['pathChanges'] as $change) {611$ref_identifiers[] = $change['commitIdentifier'];612}613614// If this ref had fewer total commits than the limit, we're safe to615// apply the constraint as a large `IN (...)` query for a list of616// commit identifiers. This is efficient.617if ($history_limit) {618if (count($ref_identifiers) >= $history_limit) {619$can_constrain = false;620break;621}622}623624$history_identifiers += array_fuse($ref_identifiers);625}626627// If all refs had a small number of ancestors, we can just put the628// constraint into the query here and we're done. Otherwise, we need629// to filter each page after it comes out of the MySQL layer.630if ($can_constrain) {631$where[] = qsprintf(632$conn,633'commit.commitIdentifier IN (%Ls)',634$history_identifiers);635} else {636$this->mustFilterRefs = true;637$this->refRepository = $repository;638}639}640641if ($this->ids !== null) {642$where[] = qsprintf(643$conn,644'commit.id IN (%Ld)',645$this->ids);646}647648if ($this->phids !== null) {649$where[] = qsprintf(650$conn,651'commit.phid IN (%Ls)',652$this->phids);653}654655if ($this->repositoryIDs !== null) {656$where[] = qsprintf(657$conn,658'commit.repositoryID IN (%Ld)',659$this->repositoryIDs);660}661662if ($this->authorPHIDs !== null) {663$author_phids = $this->authorPHIDs;664if ($author_phids) {665$author_phids = $this->selectPossibleAuthors($author_phids);666if (!$author_phids) {667throw new PhabricatorEmptyQueryException(668pht('Author PHIDs contain no possible authors.'));669}670}671672$where[] = qsprintf(673$conn,674'commit.authorPHID IN (%Ls)',675$author_phids);676}677678if ($this->epochMin !== null) {679$where[] = qsprintf(680$conn,681'commit.epoch >= %d',682$this->epochMin);683}684685if ($this->epochMax !== null) {686$where[] = qsprintf(687$conn,688'commit.epoch <= %d',689$this->epochMax);690}691692if ($this->importing !== null) {693if ($this->importing) {694$where[] = qsprintf(695$conn,696'(commit.importStatus & %d) != %d',697PhabricatorRepositoryCommit::IMPORTED_ALL,698PhabricatorRepositoryCommit::IMPORTED_ALL);699} else {700$where[] = qsprintf(701$conn,702'(commit.importStatus & %d) = %d',703PhabricatorRepositoryCommit::IMPORTED_ALL,704PhabricatorRepositoryCommit::IMPORTED_ALL);705}706}707708if ($this->identifiers !== null) {709$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;710$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;711712$refs = array();713$bare = array();714foreach ($this->identifiers as $identifier) {715$matches = null;716preg_match('/^(?:[rR]([A-Z]+:?|[0-9]+:))?(.*)$/',717$identifier, $matches);718$repo = nonempty(rtrim($matches[1], ':'), null);719$commit_identifier = nonempty($matches[2], null);720721if ($repo === null) {722if ($this->defaultRepository) {723$repo = $this->defaultRepository->getPHID();724}725}726727if ($repo === null) {728if (strlen($commit_identifier) < $min_unqualified) {729continue;730}731$bare[] = $commit_identifier;732} else {733$refs[] = array(734'repository' => $repo,735'identifier' => $commit_identifier,736);737}738}739740$sql = array();741742foreach ($bare as $identifier) {743$sql[] = qsprintf(744$conn,745'(commit.commitIdentifier LIKE %> AND '.746'LENGTH(commit.commitIdentifier) = 40)',747$identifier);748}749750if ($refs) {751$repositories = ipull($refs, 'repository');752753$repos = id(new PhabricatorRepositoryQuery())754->setViewer($this->getViewer())755->withIdentifiers($repositories);756$repos->execute();757758$repos = $repos->getIdentifierMap();759foreach ($refs as $key => $ref) {760$repo = idx($repos, $ref['repository']);761if (!$repo) {762continue;763}764765if ($repo->isSVN()) {766if (!ctype_digit((string)$ref['identifier'])) {767continue;768}769$sql[] = qsprintf(770$conn,771'(commit.repositoryID = %d AND commit.commitIdentifier = %s)',772$repo->getID(),773// NOTE: Because the 'commitIdentifier' column is a string, MySQL774// ignores the index if we hand it an integer. Hand it a string.775// See T3377.776(int)$ref['identifier']);777} else {778if (strlen($ref['identifier']) < $min_qualified) {779continue;780}781782$identifier = $ref['identifier'];783if (strlen($identifier) == 40) {784// MySQL seems to do slightly better with this version if the785// clause, so issue it if we have a full commit hash.786$sql[] = qsprintf(787$conn,788'(commit.repositoryID = %d789AND commit.commitIdentifier = %s)',790$repo->getID(),791$identifier);792} else {793$sql[] = qsprintf(794$conn,795'(commit.repositoryID = %d796AND commit.commitIdentifier LIKE %>)',797$repo->getID(),798$identifier);799}800}801}802}803804if (!$sql) {805// If we discarded all possible identifiers (e.g., they all referenced806// bogus repositories or were all too short), make sure the query finds807// nothing.808throw new PhabricatorEmptyQueryException(809pht('No commit identifiers.'));810}811812$where[] = qsprintf($conn, '%LO', $sql);813}814815if ($this->auditIDs !== null) {816$where[] = qsprintf(817$conn,818'auditor.id IN (%Ld)',819$this->auditIDs);820}821822if ($this->auditorPHIDs !== null) {823$where[] = qsprintf(824$conn,825'auditor.auditorPHID IN (%Ls)',826$this->auditorPHIDs);827}828829if ($this->statuses !== null) {830$statuses = DiffusionCommitAuditStatus::newModernKeys(831$this->statuses);832833$where[] = qsprintf(834$conn,835'commit.auditStatus IN (%Ls)',836$statuses);837}838839if ($this->packagePHIDs !== null) {840$where[] = qsprintf(841$conn,842'package.dst IN (%Ls)',843$this->packagePHIDs);844}845846if ($this->unreachable !== null) {847if ($this->unreachable) {848$where[] = qsprintf(849$conn,850'(commit.importStatus & %d) = %d',851PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,852PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);853} else {854$where[] = qsprintf(855$conn,856'(commit.importStatus & %d) = 0',857PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);858}859}860861if ($this->permanent !== null) {862if ($this->permanent) {863$where[] = qsprintf(864$conn,865'(commit.importStatus & %d) = %d',866PhabricatorRepositoryCommit::IMPORTED_PERMANENT,867PhabricatorRepositoryCommit::IMPORTED_PERMANENT);868} else {869$where[] = qsprintf(870$conn,871'(commit.importStatus & %d) = 0',872PhabricatorRepositoryCommit::IMPORTED_PERMANENT);873}874}875876return $where;877}878879protected function didFilterResults(array $filtered) {880if ($this->identifierMap) {881foreach ($this->identifierMap as $name => $commit) {882if (isset($filtered[$commit->getPHID()])) {883unset($this->identifierMap[$name]);884}885}886}887}888889private function shouldJoinAuditor() {890return ($this->auditIDs || $this->auditorPHIDs);891}892893private function shouldJoinOwners() {894return (bool)$this->packagePHIDs;895}896897protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {898$join = parent::buildJoinClauseParts($conn);899$audit_request = new PhabricatorRepositoryAuditRequest();900901if ($this->shouldJoinAuditor()) {902$join[] = qsprintf(903$conn,904'JOIN %T auditor ON commit.phid = auditor.commitPHID',905$audit_request->getTableName());906}907908if ($this->shouldJoinOwners()) {909$join[] = qsprintf(910$conn,911'JOIN %T package ON commit.phid = package.src912AND package.type = %s',913PhabricatorEdgeConfig::TABLE_NAME_EDGE,914DiffusionCommitHasPackageEdgeType::EDGECONST);915}916917return $join;918}919920protected function shouldGroupQueryResultRows() {921if ($this->shouldJoinAuditor()) {922return true;923}924925if ($this->shouldJoinOwners()) {926return true;927}928929return parent::shouldGroupQueryResultRows();930}931932public function getQueryApplicationClass() {933return 'PhabricatorDiffusionApplication';934}935936public function getOrderableColumns() {937return parent::getOrderableColumns() + array(938'epoch' => array(939'table' => $this->getPrimaryTableAlias(),940'column' => 'epoch',941'type' => 'int',942'reverse' => false,943),944);945}946947protected function newPagingMapFromPartialObject($object) {948return array(949'id' => (int)$object->getID(),950'epoch' => (int)$object->getEpoch(),951);952}953954public function getBuiltinOrders() {955$parent = parent::getBuiltinOrders();956957// Rename the default ID-based orders.958$parent['importnew'] = array(959'name' => pht('Import Date (Newest First)'),960) + $parent['newest'];961962$parent['importold'] = array(963'name' => pht('Import Date (Oldest First)'),964) + $parent['oldest'];965966return array(967'newest' => array(968'vector' => array('epoch', 'id'),969'name' => pht('Commit Date (Newest First)'),970),971'oldest' => array(972'vector' => array('-epoch', '-id'),973'name' => pht('Commit Date (Oldest First)'),974),975) + $parent;976}977978private function selectPossibleAuthors(array $phids) {979// See PHI1057. Select PHIDs which might possibly be commit authors from980// a larger list of PHIDs. This primarily filters out packages and projects981// from "Responsible Users: ..." queries. Our goal in performing this982// filtering is to improve the performance of the final query.983984foreach ($phids as $key => $phid) {985if (phid_get_type($phid) !== PhabricatorPeopleUserPHIDType::TYPECONST) {986unset($phids[$key]);987}988}989990return $phids;991}992993994}995996997