1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 14:52:40 +01:00

Improve Arcanist Mercurial compatibility

Summary:
  - Use "hg parents" to figure out where outgoing changes originate from, not
"~1", since that's a new feature in Mercurial.
  - Add "--style default" all over the place since hg config can override this
(similar to the date config issues we saw in git).
  - Cache working copy status so we don't run full hg diffs like 30 times
(similar to git/svn APIs).
  - Use full "--git" flag instead of rather cryptic "-g".

Test Plan: Ran "arc diff" in my hg working copy, got the diff I expected.

Reviewers: Makinde, aran, jungejason, nh, tuomaspelkonen

Reviewed By: Makinde

CC: aran, Makinde

Differential Revision: 934
This commit is contained in:
epriestley 2011-09-14 18:11:00 -07:00
parent 1c9746a46e
commit 9df1b8a4bd

View file

@ -69,22 +69,40 @@ class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
public function getRelativeCommit() { public function getRelativeCommit() {
if (empty($this->relativeCommit)) { if (empty($this->relativeCommit)) {
list($stdout) = execx( list($stdout) = execx(
'(cd %s && hg outgoing --branch `hg branch` --limit 1)', '(cd %s && hg outgoing --branch `hg branch` --limit 1 --style default)',
$this->getPath()); $this->getPath());
$logs = $this->parseMercurialLog($stdout); $logs = $this->parseMercurialLog($stdout);
if (!count($logs)) { if (!count($logs)) {
throw new ArcanistUsageException("You have no outgoing changes!"); throw new ArcanistUsageException("You have no outgoing changes!");
} }
$oldest_log = head($logs); $oldest_log = head($logs);
$oldest_rev = $oldest_log['rev'];
$this->relativeCommit = $oldest_log['rev'].'~1'; // NOTE: The "^" and "~" syntaxes were only added in hg 1.9, which is new
// as of July 2011, so do this in a compatible way. Also, "hg log" and
// "hg outgoing" don't necessarily show parents (even if given an explicit
// template consisting of just the parents token) so we need to separately
// execute "hg parents".
list($stdout) = execx(
'(cd %s && hg parents --style default --rev %s)',
$this->getPath(),
$oldest_rev);
$parents_logs = $this->parseMercurialLog($stdout);
$first_parent = head($parents_logs);
if (!$first_parent) {
throw new ArcanistUsageException(
"Oldest outgoing change has no parent revision!");
}
$this->relativeCommit = $first_parent['rev'];
} }
return $this->relativeCommit; return $this->relativeCommit;
} }
public function getLocalCommitInformation() { public function getLocalCommitInformation() {
list($info) = execx( list($info) = execx(
'(cd %s && hg log --rev %s..%s --)', '(cd %s && hg log --style default --rev %s..%s --)',
$this->getPath(), $this->getPath(),
$this->getRelativeCommit(), $this->getRelativeCommit(),
$this->getWorkingCopyRevision()); $this->getWorkingCopyRevision());
@ -128,57 +146,61 @@ class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
public function getWorkingCopyStatus() { public function getWorkingCopyStatus() {
// A reviewable revision spans multiple local commits in Mercurial, but if (!isset($this->status)) {
// there is no way to get file change status across multiple commits, so // A reviewable revision spans multiple local commits in Mercurial, but
// just take the entire diff and parse it to figure out what's changed. // there is no way to get file change status across multiple commits, so
// just take the entire diff and parse it to figure out what's changed.
$diff = $this->getFullMercurialDiff(); $diff = $this->getFullMercurialDiff();
$parser = new ArcanistDiffParser(); $parser = new ArcanistDiffParser();
$changes = $parser->parseDiff($diff); $changes = $parser->parseDiff($diff);
$status_map = array(); $status_map = array();
foreach ($changes as $change) { foreach ($changes as $change) {
$flags = 0; $flags = 0;
switch ($change->getType()) { switch ($change->getType()) {
case ArcanistDiffChangeType::TYPE_ADD: case ArcanistDiffChangeType::TYPE_ADD:
case ArcanistDiffChangeType::TYPE_MOVE_HERE: case ArcanistDiffChangeType::TYPE_MOVE_HERE:
case ArcanistDiffChangeType::TYPE_COPY_HERE: case ArcanistDiffChangeType::TYPE_COPY_HERE:
$flags |= self::FLAG_ADDED; $flags |= self::FLAG_ADDED;
break; break;
case ArcanistDiffChangeType::TYPE_CHANGE: case ArcanistDiffChangeType::TYPE_CHANGE:
case ArcanistDiffChangeType::TYPE_COPY_AWAY: // Check for changes? case ArcanistDiffChangeType::TYPE_COPY_AWAY: // Check for changes?
$flags |= self::FLAG_MODIFIED; $flags |= self::FLAG_MODIFIED;
break; break;
case ArcanistDiffChangeType::TYPE_DELETE: case ArcanistDiffChangeType::TYPE_DELETE:
case ArcanistDiffChangeType::TYPE_MOVE_AWAY: case ArcanistDiffChangeType::TYPE_MOVE_AWAY:
case ArcanistDiffChangeType::TYPE_MULTICOPY: case ArcanistDiffChangeType::TYPE_MULTICOPY:
$flags |= self::FLAG_DELETED; $flags |= self::FLAG_DELETED;
break; break;
}
$status_map[$change->getCurrentPath()] = $flags;
} }
$status_map[$change->getCurrentPath()] = $flags;
list($stdout) = execx(
'(cd %s && hg status)',
$this->getPath());
$working_status = $this->parseMercurialStatus($stdout);
foreach ($working_status as $path => $status) {
$status |= self::FLAG_UNCOMMITTED;
if (!empty($status_map[$path])) {
$status_map[$path] |= $status;
} else {
$status_map[$path] = $status;
}
}
$this->status = $status_map;
} }
list($stdout) = execx( return $this->status;
'(cd %s && hg status)',
$this->getPath());
$working_status = $this->parseMercurialStatus($stdout);
foreach ($working_status as $path => $status) {
$status |= self::FLAG_UNCOMMITTED;
if (!empty($status_map[$path])) {
$status_map[$path] |= $status;
} else {
$status_map[$path] = $status;
}
}
return $status_map;
} }
private function getDiffOptions() { private function getDiffOptions() {
$options = array( $options = array(
'-g', '--git',
'-U'.$this->getDiffLinesOfContext(), '-U'.$this->getDiffLinesOfContext(),
); );
return implode(' ', $options); return implode(' ', $options);