Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright 2011 Facebook, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interfaces with the Mercurial working copies.
|
|
|
|
*
|
|
|
|
* @group workingcopy
|
|
|
|
*/
|
|
|
|
class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
|
|
|
|
|
|
|
|
private $status;
|
|
|
|
private $base;
|
2011-08-09 18:54:10 -07:00
|
|
|
private $relativeCommit;
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
|
|
|
|
public function getSourceControlSystemName() {
|
|
|
|
return 'hg';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSourceControlBaseRevision() {
|
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg id -ir %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$this->getRelativeCommit());
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSourceControlPath() {
|
|
|
|
return '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBranchName() {
|
|
|
|
// TODO: I have nearly no idea how hg local branches work.
|
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg branch)',
|
|
|
|
$this->getPath());
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
2011-08-09 18:54:10 -07:00
|
|
|
public function setRelativeCommit($commit) {
|
|
|
|
list($err) = exec_manual(
|
|
|
|
'(cd %s && hg id -ir %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$commit);
|
|
|
|
|
|
|
|
if ($err) {
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"Commit '{$commit}' is not a valid Mercurial commit identifier.");
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->relativeCommit = $commit;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
public function getRelativeCommit() {
|
2011-08-09 18:54:10 -07:00
|
|
|
if (empty($this->relativeCommit)) {
|
|
|
|
list($stdout) = execx(
|
2011-09-14 18:11:00 -07:00
|
|
|
'(cd %s && hg outgoing --branch `hg branch` --limit 1 --style default)',
|
2011-08-09 18:54:10 -07:00
|
|
|
$this->getPath());
|
|
|
|
$logs = $this->parseMercurialLog($stdout);
|
|
|
|
if (!count($logs)) {
|
|
|
|
throw new ArcanistUsageException("You have no outgoing changes!");
|
|
|
|
}
|
|
|
|
$oldest_log = head($logs);
|
2011-09-14 18:11:00 -07:00
|
|
|
$oldest_rev = $oldest_log['rev'];
|
2011-08-09 18:54:10 -07:00
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
// 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'];
|
2011-08-09 18:54:10 -07:00
|
|
|
}
|
|
|
|
return $this->relativeCommit;
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2011-08-23 18:48:55 -07:00
|
|
|
public function getLocalCommitInformation() {
|
|
|
|
list($info) = execx(
|
2011-09-14 18:11:00 -07:00
|
|
|
'(cd %s && hg log --style default --rev %s..%s --)',
|
2011-08-23 18:48:55 -07:00
|
|
|
$this->getPath(),
|
|
|
|
$this->getRelativeCommit(),
|
2011-09-14 19:08:16 -07:00
|
|
|
$this->getWorkingCopyRevision());
|
2011-08-31 11:51:56 -07:00
|
|
|
$logs = $this->parseMercurialLog($info);
|
|
|
|
|
|
|
|
// Get rid of the first log, it's not actually part of the diff. "hg log"
|
|
|
|
// is inclusive, while "hg diff" is exclusive.
|
|
|
|
array_shift($logs);
|
|
|
|
|
|
|
|
return $logs;
|
2011-08-23 18:48:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
public function getBlame($path) {
|
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg blame -u -v -c --rev %s -- %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$this->getRelativeCommit(),
|
|
|
|
$path);
|
|
|
|
|
|
|
|
$blame = array();
|
|
|
|
foreach (explode("\n", trim($stdout)) as $line) {
|
|
|
|
if (!strlen($line)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$matches = null;
|
|
|
|
$ok = preg_match('^/\s*([^:]+?) [a-f0-9]{12}: (.*)$/', $line, $matches);
|
|
|
|
|
|
|
|
if (!$ok) {
|
|
|
|
throw new Exception("Unable to parse Mercurial blame line: {$line}");
|
|
|
|
}
|
|
|
|
|
|
|
|
$revision = $matches[2];
|
|
|
|
$author = trim($matches[1]);
|
|
|
|
$blame[] = array($author, $revision);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $blame;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getWorkingCopyStatus() {
|
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
if (!isset($this->status)) {
|
|
|
|
// A reviewable revision spans multiple local commits in Mercurial, but
|
|
|
|
// 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();
|
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
$changes = $parser->parseDiff($diff);
|
|
|
|
|
|
|
|
$status_map = array();
|
|
|
|
|
|
|
|
foreach ($changes as $change) {
|
|
|
|
$flags = 0;
|
|
|
|
switch ($change->getType()) {
|
|
|
|
case ArcanistDiffChangeType::TYPE_ADD:
|
|
|
|
case ArcanistDiffChangeType::TYPE_MOVE_HERE:
|
|
|
|
case ArcanistDiffChangeType::TYPE_COPY_HERE:
|
|
|
|
$flags |= self::FLAG_ADDED;
|
|
|
|
break;
|
|
|
|
case ArcanistDiffChangeType::TYPE_CHANGE:
|
|
|
|
case ArcanistDiffChangeType::TYPE_COPY_AWAY: // Check for changes?
|
|
|
|
$flags |= self::FLAG_MODIFIED;
|
|
|
|
break;
|
|
|
|
case ArcanistDiffChangeType::TYPE_DELETE:
|
|
|
|
case ArcanistDiffChangeType::TYPE_MOVE_AWAY:
|
|
|
|
case ArcanistDiffChangeType::TYPE_MULTICOPY:
|
|
|
|
$flags |= self::FLAG_DELETED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$status_map[$change->getCurrentPath()] = $flags;
|
2011-08-09 18:54:10 -07:00
|
|
|
}
|
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg status)',
|
|
|
|
$this->getPath());
|
2011-08-09 18:54:10 -07:00
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
$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;
|
|
|
|
}
|
2011-08-09 18:54:10 -07:00
|
|
|
}
|
2011-09-14 18:11:00 -07:00
|
|
|
|
|
|
|
$this->status = $status_map;
|
2011-08-09 18:54:10 -07:00
|
|
|
}
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
return $this->status;
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getDiffOptions() {
|
|
|
|
$options = array(
|
2011-09-14 18:11:00 -07:00
|
|
|
'--git',
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
'-U'.$this->getDiffLinesOfContext(),
|
|
|
|
);
|
|
|
|
return implode(' ', $options);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getRawDiffText($path) {
|
|
|
|
$options = $this->getDiffOptions();
|
|
|
|
|
|
|
|
list($stdout) = execx(
|
2011-09-14 19:08:16 -07:00
|
|
|
'(cd %s && hg diff %C --rev %s --rev %s -- %s)',
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
$this->getPath(),
|
|
|
|
$options,
|
|
|
|
$this->getRelativeCommit(),
|
2011-09-14 19:08:16 -07:00
|
|
|
$this->getWorkingCopyRevision(),
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
$path);
|
|
|
|
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFullMercurialDiff() {
|
|
|
|
$options = $this->getDiffOptions();
|
|
|
|
|
|
|
|
list($stdout) = execx(
|
2011-09-14 19:08:16 -07:00
|
|
|
'(cd %s && hg diff %C --rev %s --rev %s --)',
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
$this->getPath(),
|
|
|
|
$options,
|
2011-09-14 19:08:16 -07:00
|
|
|
$this->getRelativeCommit(),
|
|
|
|
$this->getWorkingCopyRevision());
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getOriginalFileData($path) {
|
|
|
|
return $this->getFileDataAtRevision($path, $this->getRelativeCommit());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCurrentFileData($path) {
|
2011-09-14 19:08:16 -07:00
|
|
|
return $this->getFileDataAtRevision(
|
|
|
|
$path,
|
|
|
|
$this->getWorkingCopyRevision());
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getFileDataAtRevision($path, $revision) {
|
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg cat --rev %s -- %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$path);
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
2011-08-09 18:54:10 -07:00
|
|
|
private function parseMercurialStatus($status) {
|
|
|
|
$result = array();
|
|
|
|
|
|
|
|
$status = trim($status);
|
|
|
|
if (!strlen($status)) {
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lines = explode("\n", $status);
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
$flags = 0;
|
|
|
|
list($code, $path) = explode(' ', $line, 2);
|
|
|
|
switch ($code) {
|
|
|
|
case 'A':
|
|
|
|
$flags |= self::FLAG_ADDED;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
$flags |= self::FLAG_REMOVED;
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
$flags |= self::FLAG_MODIFIED;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
// This is "clean" and included only for completeness, these files
|
|
|
|
// have not been changed.
|
|
|
|
break;
|
|
|
|
case '!':
|
|
|
|
$flags |= self::FLAG_MISSING;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
$flags |= self::FLAG_UNTRACKED;
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
// This is "ignored" and included only for completeness.
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception("Unknown Mercurial status '{$code}'.");
|
|
|
|
}
|
|
|
|
|
|
|
|
$result[$path] = $flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function parseMercurialLog($log) {
|
|
|
|
$result = array();
|
|
|
|
|
|
|
|
$chunks = explode("\n\n", trim($log));
|
|
|
|
foreach ($chunks as $chunk) {
|
|
|
|
$commit = array();
|
|
|
|
$lines = explode("\n", $chunk);
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
if (preg_match('/^(comparing with|searching for changes)/', $line)) {
|
|
|
|
// These are sent to stdout when you run "hg outgoing" although the
|
|
|
|
// format is otherwise identical to "hg log".
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
list($name, $value) = explode(':', $line, 2);
|
|
|
|
$value = trim($value);
|
|
|
|
switch ($name) {
|
|
|
|
case 'user':
|
|
|
|
$commit['user'] = $value;
|
|
|
|
break;
|
|
|
|
case 'date':
|
|
|
|
$commit['date'] = strtotime($value);
|
|
|
|
break;
|
|
|
|
case 'summary':
|
|
|
|
$commit['summary'] = $value;
|
|
|
|
break;
|
|
|
|
case 'changeset':
|
|
|
|
list($local, $rev) = explode(':', $value, 2);
|
|
|
|
$commit['local'] = $local;
|
|
|
|
$commit['rev'] = $rev;
|
|
|
|
break;
|
2011-08-23 18:48:55 -07:00
|
|
|
case 'parent':
|
|
|
|
if (empty($commit['parents'])) {
|
|
|
|
$commit['parents'] = array();
|
|
|
|
}
|
|
|
|
list($local, $rev) = explode(':', $value, 2);
|
|
|
|
$commit['parents'][] = array(
|
|
|
|
'local' => $local,
|
|
|
|
'rev' => $rev,
|
|
|
|
);
|
|
|
|
break;
|
2011-08-31 11:51:56 -07:00
|
|
|
case 'branch':
|
|
|
|
$commit['branch'] = $value;
|
|
|
|
break;
|
|
|
|
case 'tag':
|
|
|
|
$commit['tag'] = $value;
|
|
|
|
break;
|
2011-08-09 18:54:10 -07:00
|
|
|
default:
|
|
|
|
throw new Exception("Unknown Mercurial log field '{$name}'!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$result[] = $commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2011-09-14 19:08:16 -07:00
|
|
|
private function getWorkingCopyRevision() {
|
|
|
|
// In Mercurial, "tip" means the tip of the current branch, not what's in
|
|
|
|
// the working copy. The tip may be ahead of the working copy. We need to
|
|
|
|
// use "hg summary" to figure out what is actually in the working copy.
|
|
|
|
// For instance, "hg up 4 && arc diff" should not show commits 5 and above.
|
|
|
|
|
|
|
|
// The output of "hg summary" is different from the output of other hg
|
|
|
|
// commands so just parse it manually.
|
|
|
|
list($stdout) = execx(
|
|
|
|
'(cd %s && hg summary)',
|
|
|
|
$this->getPath());
|
|
|
|
$lines = explode("\n", $stdout);
|
|
|
|
|
|
|
|
$first = head($lines);
|
|
|
|
$match = null;
|
|
|
|
if (!preg_match('/^parent: \d+:([^ ]+)( |$)/', $first, $match)) {
|
|
|
|
throw new Exception("Unable to parse 'hg summary'.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return trim($match[1]);
|
|
|
|
}
|
|
|
|
|
2011-09-14 18:44:54 -07:00
|
|
|
public function supportsRelativeLocalCommits() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function parseRelativeLocalCommit(array $argv) {
|
|
|
|
if (count($argv) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (count($argv) != 1) {
|
|
|
|
throw new ArcanistUsageException("Specify only one commit.");
|
|
|
|
}
|
|
|
|
// This does the "hg id" call we need to normalize/validate the revision
|
|
|
|
// identifier.
|
|
|
|
$this->setRelativeCommit(reset($argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAllLocalChanges() {
|
|
|
|
$diff = $this->getFullMercurialDiff();
|
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
return $parser->parseDiff($diff);
|
|
|
|
}
|
|
|
|
|
Add an "arc merge" workflow
Summary:
This should support conservative rewrite policies in git fairly well, under an
assumed workflow of:
- Develop in local branches, never rewrite history.
- Commit with "-m" or by typing a brief, non-template commit message
describing the checkpoint.
- Provide rich information in the web console (reviewers, etc.)
- Finalize with "git checkout master && arc merge branch && git push" or some
flavor thereof.
This supports Mercurial somewhat. The major problem is that "hg merge" fails if
the local is a fastforward of the remote, at which point there's nowhere we can
throw the commit message. Oh well. Just push it and we'll do our best to link
them up based on local commit info.
I am increasingly forming an opinion that Mercurial is "saftey-scissors git".
But also maybe I have no clue what I'm doing. I just don't understand why anyone
would think it's a good idea to have a trunk consisting of ~50% known-broken
revisions, random checkpoint parts, whitespace changes, typo fixes, etc. If you
use git with branching you can avoid this by making a trunk out of merges or
with rebase/amend, but there seems to be no way to have "one commit = one idea"
in any real sense in Mercurial.
Test Plan: Execute "arc merge" in git and mercurial.
Reviewers: fratrik, Makinde, aran, jungejason, tuomaspelkonen
Reviewed By: Makinde
CC: aran, epriestley, Makinde
Differential Revision: 860
2011-08-25 16:02:03 -07:00
|
|
|
public function supportsLocalBranchMerge() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function performLocalBranchMerge($branch, $message) {
|
|
|
|
if ($branch) {
|
|
|
|
$err = phutil_passthru(
|
|
|
|
'(cd %s && hg merge --rev %s && hg commit -m %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$branch,
|
|
|
|
$message);
|
|
|
|
} else {
|
|
|
|
$err = phutil_passthru(
|
|
|
|
'(cd %s && hg merge && hg commit -m %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$message);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($err) {
|
|
|
|
throw new ArcanistUsageException("Merge failed!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFinalizedRevisionMessage() {
|
|
|
|
return "You may now push this commit upstream, as appropriate (e.g. with ".
|
|
|
|
"'hg push' or by printing and faxing it).";
|
|
|
|
}
|
|
|
|
|
Basic Mercurial support for Arcanist
Summary:
There's a lot of ground left to cover but this makes "arc diff" work (on one
trivial diff) in my sandbox, at least, and supports parsing of Mercurial native
diffs (which are unified + a custom header). Piles of missing features, still.
Some of this is blocked by me not understanding the mercurial model well yet.
This is also a really good opportunity for cleanup (especially, reducing the
level of "instanceof" in the diff workflow), I'll try to do a bunch of that in
followup diffs.
Test Plan: Ran "arc diff" in a mercurial repository, got a diff out of it.
Reviewed By: aran
Reviewers: Makinde, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock, fratrik
Differential Revision: 792
2011-08-09 09:00:29 -07:00
|
|
|
}
|