Path: blob/master/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
12242 views
<?php12final class DiffusionHistoryQueryConduitAPIMethod3extends DiffusionQueryConduitAPIMethod {45private $parents = array();67public function getAPIMethodName() {8return 'diffusion.historyquery';9}1011public function getMethodDescription() {12return pht(13'Returns history information for a repository at a specific '.14'commit and path.');15}1617protected function defineReturnType() {18return 'array';19}2021protected function defineCustomParamTypes() {22return array(23'commit' => 'required string',24'against' => 'optional string',25'path' => 'required string',26'offset' => 'required int',27'limit' => 'required int',28'needDirectChanges' => 'optional bool',29'needChildChanges' => 'optional bool',30);31}3233protected function getResult(ConduitAPIRequest $request) {34$path_changes = parent::getResult($request);3536return array(37'pathChanges' => mpull($path_changes, 'toDictionary'),38'parents' => $this->parents,39);40}4142protected function getGitResult(ConduitAPIRequest $request) {43$drequest = $this->getDiffusionRequest();44$repository = $drequest->getRepository();45$commit_hash = $request->getValue('commit');46$against_hash = $request->getValue('against');47$offset = $request->getValue('offset');48$limit = $request->getValue('limit');4950$path = $request->getValue('path');51if ($path === null || !strlen($path)) {52$path = null;53}5455if ($against_hash !== null && strlen($against_hash)) {56$commit_range = "{$against_hash}..{$commit_hash}";57} else {58$commit_range = $commit_hash;59}6061$argv = array();6263$argv[] = '--skip';64$argv[] = $offset;6566$argv[] = '--max-count';67$argv[] = $limit;6869$argv[] = '--format=%H:%P';7071$argv[] = gitsprintf('%s', $commit_range);7273$argv[] = '--';7475if ($path !== null) {76$argv[] = $path;77}7879list($stdout) = $repository->execxLocalCommand(80'log %Ls',81$argv);8283$lines = explode("\n", trim($stdout));84$lines = array_filter($lines);8586$hash_list = array();87$parent_map = array();88foreach ($lines as $line) {89list($hash, $parents) = explode(':', $line);90$hash_list[] = $hash;91$parent_map[$hash] = preg_split('/\s+/', $parents);92}9394$this->parents = $parent_map;9596if (!$hash_list) {97return array();98}99100return DiffusionQuery::loadHistoryForCommitIdentifiers(101$hash_list,102$drequest);103}104105protected function getMercurialResult(ConduitAPIRequest $request) {106$drequest = $this->getDiffusionRequest();107$repository = $drequest->getRepository();108$commit_hash = $request->getValue('commit');109$path = $request->getValue('path');110$offset = $request->getValue('offset');111$limit = $request->getValue('limit');112113$path = DiffusionPathIDQuery::normalizePath($path);114$path = ltrim($path, '/');115116// NOTE: Older versions of Mercurial give different results for these117// commands (see T1268):118//119// $ hg log -- ''120// $ hg log121//122// All versions of Mercurial give different results for these commands123// (merge commits are excluded with the "." version):124//125// $ hg log -- .126// $ hg log127//128// If we don't have a path component in the query, omit it from the command129// entirely to avoid these inconsistencies.130131// NOTE: When viewing the history of a file, we don't use "-b", because132// Mercurial stops history at the branchpoint but we're interested in all133// ancestors. When viewing history of a branch, we do use "-b", and thus134// stop history (this is more consistent with the Mercurial worldview of135// branches).136137$path_args = array();138if (strlen($path)) {139$path_args[] = $path;140$revset_arg = hgsprintf(141'reverse(ancestors(%s))',142$commit_hash);143} else {144$revset_arg = hgsprintf(145'reverse(ancestors(%s)) and branch(%s)',146$commit_hash,147$drequest->getBranch());148}149150$hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg');151if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) {152$hg_log_template = '{node} {p1.node} {p2.node}\\n';153} else {154$hg_log_template = '{node} {p1node} {p2node}\\n';155}156157list($stdout) = $repository->execxLocalCommand(158'log --template %s --limit %d --rev %s -- %Ls',159$hg_log_template,160($offset + $limit), // No '--skip' in Mercurial.161$revset_arg,162$path_args);163164$lines = explode("\n", trim($stdout));165$lines = array_slice($lines, $offset);166167$hash_list = array();168$parent_map = array();169170$last = null;171foreach (array_reverse($lines) as $line) {172$parts = explode(' ', trim($line));173$hash = $parts[0];174$parents = array_slice($parts, 1, 2);175foreach ($parents as $parent) {176if (!preg_match('/^0+\z/', $parent)) {177$parent_map[$hash][] = $parent;178}179}180// This may happen for the zeroth commit in repository, both hashes181// are "000000000...".182if (empty($parent_map[$hash])) {183$parent_map[$hash] = array('...');184}185186// The rendering code expects the first commit to be "mainline", like187// Git. Flip the order so it does the right thing.188$parent_map[$hash] = array_reverse($parent_map[$hash]);189190$hash_list[] = $hash;191$last = $hash;192}193194$hash_list = array_reverse($hash_list);195$this->parents = array_reverse($parent_map, true);196197return DiffusionQuery::loadHistoryForCommitIdentifiers(198$hash_list,199$drequest);200}201202protected function getSVNResult(ConduitAPIRequest $request) {203$drequest = $this->getDiffusionRequest();204$repository = $drequest->getRepository();205$commit = $request->getValue('commit');206$path = $request->getValue('path');207$offset = $request->getValue('offset');208$limit = $request->getValue('limit');209$need_direct_changes = $request->getValue('needDirectChanges');210$need_child_changes = $request->getValue('needChildChanges');211212$conn_r = $repository->establishConnection('r');213214$paths = queryfx_all(215$conn_r,216'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',217PhabricatorRepository::TABLE_PATH,218array(md5('/'.trim($path, '/'))));219$paths = ipull($paths, 'id', 'path');220$path_id = idx($paths, '/'.trim($path, '/'));221222if (!$path_id) {223return array();224}225226$filter_query = qsprintf($conn_r, '');227if ($need_direct_changes) {228if ($need_child_changes) {229$filter_query = qsprintf(230$conn_r,231'AND (isDirect = 1 OR changeType = %s)',232DifferentialChangeType::TYPE_CHILD);233} else {234$filter_query = qsprintf(235$conn_r,236'AND (isDirect = 1)');237}238}239240$history_data = queryfx_all(241$conn_r,242'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d243AND commitSequence <= %d244%Q245ORDER BY commitSequence DESC246LIMIT %d, %d',247PhabricatorRepository::TABLE_PATHCHANGE,248$repository->getID(),249$path_id,250$commit ? $commit : 0x7FFFFFFF,251$filter_query,252$offset,253$limit);254255$commits = array();256$commit_data = array();257258$commit_ids = ipull($history_data, 'commitID');259if ($commit_ids) {260$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(261'id IN (%Ld)',262$commit_ids);263if ($commits) {264$commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(265'commitID in (%Ld)',266$commit_ids);267$commit_data = mpull($commit_data, null, 'getCommitID');268}269}270271$history = array();272foreach ($history_data as $row) {273$item = new DiffusionPathChange();274275$commit = idx($commits, $row['commitID']);276if ($commit) {277$item->setCommit($commit);278$item->setCommitIdentifier($commit->getCommitIdentifier());279$data = idx($commit_data, $commit->getID());280if ($data) {281$item->setCommitData($data);282}283}284285$item->setChangeType($row['changeType']);286$item->setFileType($row['fileType']);287288$history[] = $item;289}290291return $history;292}293294}295296297