3 final class DiffusionHistoryQueryConduitAPIMethod
4 extends DiffusionQueryConduitAPIMethod
{
6 private $parents = array();
8 public function getAPIMethodName() {
9 return 'diffusion.historyquery';
12 public function getMethodDescription() {
14 'Returns history information for a repository at a specific '.
18 protected function defineReturnType() {
22 protected function defineCustomParamTypes() {
24 'commit' => 'required string',
25 'against' => 'optional string',
26 'path' => 'required string',
27 'offset' => 'required int',
28 'limit' => 'required int',
29 'needDirectChanges' => 'optional bool',
30 'needChildChanges' => 'optional bool',
34 protected function getResult(ConduitAPIRequest
$request) {
35 $path_changes = parent
::getResult($request);
38 'pathChanges' => mpull($path_changes, 'toDictionary'),
39 'parents' => $this->parents
,
43 protected function getGitResult(ConduitAPIRequest
$request) {
44 $drequest = $this->getDiffusionRequest();
45 $repository = $drequest->getRepository();
46 $commit_hash = $request->getValue('commit');
47 $against_hash = $request->getValue('against');
49 $path = $request->getValue('path');
54 $offset = $request->getValue('offset');
55 $limit = $request->getValue('limit');
57 if (strlen($against_hash)) {
58 $commit_range = "${against_hash}..${commit_hash}";
60 $commit_range = $commit_hash;
68 $argv[] = '--max-count';
71 $argv[] = '--format=%H:%P';
73 $argv[] = gitsprintf('%s', $commit_range);
81 list($stdout) = $repository->execxLocalCommand(
85 $lines = explode("\n", trim($stdout));
86 $lines = array_filter($lines);
89 $parent_map = array();
90 foreach ($lines as $line) {
91 list($hash, $parents) = explode(':', $line);
93 $parent_map[$hash] = preg_split('/\s+/', $parents);
96 $this->parents
= $parent_map;
102 return DiffusionQuery
::loadHistoryForCommitIdentifiers(
107 protected function getMercurialResult(ConduitAPIRequest
$request) {
108 $drequest = $this->getDiffusionRequest();
109 $repository = $drequest->getRepository();
110 $commit_hash = $request->getValue('commit');
111 $path = $request->getValue('path');
112 $offset = $request->getValue('offset');
113 $limit = $request->getValue('limit');
115 $path = DiffusionPathIDQuery
::normalizePath($path);
116 $path = ltrim($path, '/');
118 // NOTE: Older versions of Mercurial give different results for these
119 // commands (see T1268):
124 // All versions of Mercurial give different results for these commands
125 // (merge commits are excluded with the "." version):
130 // If we don't have a path component in the query, omit it from the command
131 // entirely to avoid these inconsistencies.
133 // NOTE: When viewing the history of a file, we don't use "-b", because
134 // Mercurial stops history at the branchpoint but we're interested in all
135 // ancestors. When viewing history of a branch, we do use "-b", and thus
136 // stop history (this is more consistent with the Mercurial worldview of
139 $path_args = array();
141 $path_args[] = $path;
142 $revset_arg = hgsprintf(
143 'reverse(ancestors(%s))',
146 $revset_arg = hgsprintf(
147 'reverse(ancestors(%s)) and branch(%s)',
149 $drequest->getBranch());
152 $hg_analyzer = PhutilBinaryAnalyzer
::getForBinary('hg');
153 if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) {
154 $hg_log_template = '{node} {p1.node} {p2.node}\\n';
156 $hg_log_template = '{node} {p1node} {p2node}\\n';
159 list($stdout) = $repository->execxLocalCommand(
160 'log --template %s --limit %d --rev %s -- %Ls',
162 ($offset +
$limit), // No '--skip' in Mercurial.
166 $lines = explode("\n", trim($stdout));
167 $lines = array_slice($lines, $offset);
169 $hash_list = array();
170 $parent_map = array();
173 foreach (array_reverse($lines) as $line) {
174 $parts = explode(' ', trim($line));
176 $parents = array_slice($parts, 1, 2);
177 foreach ($parents as $parent) {
178 if (!preg_match('/^0+\z/', $parent)) {
179 $parent_map[$hash][] = $parent;
182 // This may happen for the zeroth commit in repository, both hashes
183 // are "000000000...".
184 if (empty($parent_map[$hash])) {
185 $parent_map[$hash] = array('...');
188 // The rendering code expects the first commit to be "mainline", like
189 // Git. Flip the order so it does the right thing.
190 $parent_map[$hash] = array_reverse($parent_map[$hash]);
192 $hash_list[] = $hash;
196 $hash_list = array_reverse($hash_list);
197 $this->parents
= array_reverse($parent_map, true);
199 return DiffusionQuery
::loadHistoryForCommitIdentifiers(
204 protected function getSVNResult(ConduitAPIRequest
$request) {
205 $drequest = $this->getDiffusionRequest();
206 $repository = $drequest->getRepository();
207 $commit = $request->getValue('commit');
208 $path = $request->getValue('path');
209 $offset = $request->getValue('offset');
210 $limit = $request->getValue('limit');
211 $need_direct_changes = $request->getValue('needDirectChanges');
212 $need_child_changes = $request->getValue('needChildChanges');
214 $conn_r = $repository->establishConnection('r');
216 $paths = queryfx_all(
218 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',
219 PhabricatorRepository
::TABLE_PATH
,
220 array(md5('/'.trim($path, '/'))));
221 $paths = ipull($paths, 'id', 'path');
222 $path_id = idx($paths, '/'.trim($path, '/'));
228 $filter_query = qsprintf($conn_r, '');
229 if ($need_direct_changes) {
230 if ($need_child_changes) {
231 $filter_query = qsprintf(
233 'AND (isDirect = 1 OR changeType = %s)',
234 DifferentialChangeType
::TYPE_CHILD
);
236 $filter_query = qsprintf(
238 'AND (isDirect = 1)');
242 $history_data = queryfx_all(
244 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d
245 AND commitSequence <= %d
247 ORDER BY commitSequence DESC
249 PhabricatorRepository
::TABLE_PATHCHANGE
,
250 $repository->getID(),
252 $commit ?
$commit : 0x7FFFFFFF,
258 $commit_data = array();
260 $commit_ids = ipull($history_data, 'commitID');
262 $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
266 $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
269 $commit_data = mpull($commit_data, null, 'getCommitID');
274 foreach ($history_data as $row) {
275 $item = new DiffusionPathChange();
277 $commit = idx($commits, $row['commitID']);
279 $item->setCommit($commit);
280 $item->setCommitIdentifier($commit->getCommitIdentifier());
281 $data = idx($commit_data, $commit->getID());
283 $item->setCommitData($data);
287 $item->setChangeType($row['changeType']);
288 $item->setFileType($row['fileType']);