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
|
|
|
|
|
|
|
|
/*
|
2012-01-06 13:14:41 -08:00
|
|
|
* Copyright 2012 Facebook, Inc.
|
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
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2012-01-31 12:07:05 -08:00
|
|
|
final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
|
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 $status;
|
|
|
|
private $base;
|
2011-08-09 18:54:10 -07:00
|
|
|
private $relativeCommit;
|
2012-07-01 11:06:05 -07:00
|
|
|
private $branch;
|
2012-02-28 16:59:40 -08:00
|
|
|
private $workingCopyRevision;
|
|
|
|
private $localCommitInfo;
|
2012-03-19 19:17:10 -07:00
|
|
|
private $includeDirectoryStateInDiffs;
|
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
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
protected function buildLocalFuture(array $argv) {
|
2012-02-28 16:56:57 -08:00
|
|
|
|
|
|
|
// Mercurial has a "defaults" feature which basically breaks automation by
|
|
|
|
// allowing the user to add random flags to any command. This feature is
|
|
|
|
// "deprecated" and "a bad idea" that you should "forget ... existed"
|
|
|
|
// according to project lead Matt Mackall:
|
|
|
|
//
|
|
|
|
// http://markmail.org/message/hl3d6eprubmkkqh5
|
|
|
|
//
|
|
|
|
// There is an HGPLAIN environmental variable which enables "plain mode"
|
|
|
|
// and hopefully disables this stuff.
|
|
|
|
|
2012-05-02 12:40:02 -07:00
|
|
|
if (phutil_is_windows()) {
|
|
|
|
$argv[0] = 'set HGPLAIN=1 & hg '.$argv[0];
|
|
|
|
} else {
|
|
|
|
$argv[0] = 'HGPLAIN=1 hg '.$argv[0];
|
|
|
|
}
|
2012-02-28 16:56:57 -08:00
|
|
|
|
|
|
|
$future = newv('ExecFuture', $argv);
|
|
|
|
$future->setCWD($this->getPath());
|
|
|
|
return $future;
|
|
|
|
}
|
|
|
|
|
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';
|
|
|
|
}
|
|
|
|
|
2012-06-12 12:39:15 -07:00
|
|
|
public function getMetadataPath() {
|
|
|
|
return $this->getPath('.hg');
|
|
|
|
}
|
|
|
|
|
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 getSourceControlBaseRevision() {
|
2012-03-19 19:17:10 -07:00
|
|
|
return $this->getCanonicalRevisionName($this->getRelativeCommit());
|
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
|
|
|
}
|
|
|
|
|
2012-03-09 14:40:47 -08:00
|
|
|
public function getCanonicalRevisionName($string) {
|
2012-03-19 19:17:10 -07:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'log -l 1 --template %s -r %s --',
|
|
|
|
'{node}',
|
2012-03-21 11:07:15 -07:00
|
|
|
$string);
|
2012-03-19 19:17:10 -07:00
|
|
|
return $stdout;
|
2012-03-09 14:40:47 -08: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 getSourceControlPath() {
|
|
|
|
return '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBranchName() {
|
2012-07-01 11:06:05 -07:00
|
|
|
if (!$this->branch) {
|
|
|
|
list($stdout) = $this->execxLocal('branch');
|
|
|
|
$this->branch = trim($stdout);
|
|
|
|
}
|
|
|
|
return $this->branch;
|
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-09 18:54:10 -07:00
|
|
|
public function setRelativeCommit($commit) {
|
2012-03-19 19:17:10 -07:00
|
|
|
try {
|
|
|
|
$commit = $this->getCanonicalRevisionName($commit);
|
|
|
|
} catch (Exception $ex) {
|
2011-08-09 18:54:10 -07:00
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"Commit '{$commit}' is not a valid Mercurial commit identifier.");
|
|
|
|
}
|
2012-03-19 19:17:10 -07:00
|
|
|
|
2011-08-09 18:54:10 -07:00
|
|
|
$this->relativeCommit = $commit;
|
2012-07-01 11:06:05 -07:00
|
|
|
$this->status = null;
|
|
|
|
$this->localCommitInfo = null;
|
2012-03-22 17:22:52 -07:00
|
|
|
|
2011-08-09 18:54:10 -07:00
|
|
|
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)) {
|
2012-06-21 18:12:09 -07:00
|
|
|
|
|
|
|
if ($this->getBaseCommitArgumentRules() ||
|
2012-06-22 16:25:49 -03:00
|
|
|
$this->getWorkingCopyIdentity()->getConfigFromAnySource('base')) {
|
2012-06-21 18:12:09 -07:00
|
|
|
$base = $this->resolveBaseCommit();
|
|
|
|
if (!$base) {
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"None of the rules in your 'base' configuration matched a valid ".
|
|
|
|
"commit. Adjust rules or specify which commit you want to use ".
|
|
|
|
"explicitly.");
|
|
|
|
}
|
|
|
|
$this->relativeCommit = $base;
|
|
|
|
return $this->relativeCommit;
|
|
|
|
}
|
|
|
|
|
2012-03-19 19:17:10 -07:00
|
|
|
list($err, $stdout) = $this->execManualLocal(
|
2012-07-01 11:06:05 -07:00
|
|
|
'outgoing --branch %s --style default',
|
|
|
|
$this->getBranchName());
|
2012-03-19 19:17:10 -07:00
|
|
|
|
|
|
|
if (!$err) {
|
|
|
|
$logs = ArcanistMercurialParser::parseMercurialLog($stdout);
|
|
|
|
} else {
|
|
|
|
// Mercurial (in some versions?) raises an error when there's nothing
|
|
|
|
// outgoing.
|
|
|
|
$logs = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$logs) {
|
2012-05-11 06:07:33 -07:00
|
|
|
|
2012-06-14 12:02:41 -07:00
|
|
|
$this->setBaseCommitExplanation(
|
2012-05-11 06:07:33 -07:00
|
|
|
"you have no outgoing commits, so arc assumes you intend to submit ".
|
2012-06-14 12:02:41 -07:00
|
|
|
"uncommitted changes in the working copy.");
|
2012-03-19 19:17:10 -07:00
|
|
|
// In Mercurial, we support operations against uncommitted changes.
|
2012-05-04 15:46:10 -07:00
|
|
|
$this->setRelativeCommit($this->getWorkingCopyRevision());
|
|
|
|
return $this->relativeCommit;
|
2011-08-09 18:54:10 -07:00
|
|
|
}
|
|
|
|
|
2012-01-06 13:14:41 -08:00
|
|
|
$outgoing_revs = ipull($logs, 'rev');
|
|
|
|
|
|
|
|
// This is essentially an implementation of a theoretical `hg merge-base`
|
|
|
|
// command.
|
2012-03-19 19:17:10 -07:00
|
|
|
$against = $this->getWorkingCopyRevision();
|
2012-01-06 13:14:41 -08:00
|
|
|
while (true) {
|
|
|
|
// 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".
|
|
|
|
|
2012-02-28 16:56:57 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'parents --style default --rev %s',
|
2012-01-06 13:14:41 -08:00
|
|
|
$against);
|
|
|
|
$parents_logs = ArcanistMercurialParser::parseMercurialLog($stdout);
|
|
|
|
|
|
|
|
list($p1, $p2) = array_merge($parents_logs, array(null, null));
|
|
|
|
|
|
|
|
if ($p1 && !in_array($p1['rev'], $outgoing_revs)) {
|
|
|
|
$against = $p1['rev'];
|
|
|
|
break;
|
|
|
|
} else if ($p2 && !in_array($p2['rev'], $outgoing_revs)) {
|
|
|
|
$against = $p2['rev'];
|
|
|
|
break;
|
|
|
|
} else if ($p1) {
|
|
|
|
$against = $p1['rev'];
|
|
|
|
} else {
|
|
|
|
// This is the case where you have a new repository and the entire
|
|
|
|
// thing is outgoing; Mercurial literally accepts "--rev null" as
|
|
|
|
// meaning "diff against the empty state".
|
|
|
|
$against = 'null';
|
|
|
|
break;
|
|
|
|
}
|
2011-09-14 18:11:00 -07:00
|
|
|
}
|
|
|
|
|
2012-05-11 06:07:33 -07:00
|
|
|
if ($against == 'null') {
|
2012-06-14 12:02:41 -07:00
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"this is a new repository (all changes are outgoing).");
|
2012-05-11 06:07:33 -07:00
|
|
|
} else {
|
2012-06-14 12:02:41 -07:00
|
|
|
$this->setBaseCommitExplanation(
|
2012-05-11 06:07:33 -07:00
|
|
|
"it is the first commit reachable from the working copy state ".
|
2012-06-14 12:02:41 -07:00
|
|
|
"which is not outgoing.");
|
2012-05-11 06:07:33 -07:00
|
|
|
}
|
|
|
|
|
2012-03-19 19:17:10 -07:00
|
|
|
$this->setRelativeCommit($against);
|
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() {
|
2012-02-28 16:59:40 -08:00
|
|
|
if ($this->localCommitInfo === null) {
|
|
|
|
list($info) = $this->execxLocal(
|
2012-07-01 11:06:05 -07:00
|
|
|
"log --template '%C' --rev %s --branch %s --",
|
2012-05-09 15:56:16 -07:00
|
|
|
"{node}\1{rev}\1{author}\1{date|rfc822date}\1".
|
|
|
|
"{branch}\1{tag}\1{parents}\1{desc}\2",
|
2012-07-01 11:06:05 -07:00
|
|
|
'(ancestors(.) - ancestors('.$this->getRelativeCommit().'))',
|
2012-05-11 13:52:14 -07:00
|
|
|
$this->getBranchName());
|
2012-05-09 15:56:16 -07:00
|
|
|
$logs = array_filter(explode("\2", $info));
|
2012-02-28 16:59:40 -08:00
|
|
|
|
2012-05-09 15:56:16 -07:00
|
|
|
$last_node = null;
|
|
|
|
|
|
|
|
$futures = array();
|
|
|
|
|
|
|
|
$commits = array();
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
list($node, $rev, $author, $date, $branch, $tag, $parents, $desc) =
|
|
|
|
explode("\1", $log);
|
|
|
|
|
|
|
|
// NOTE: If a commit has only one parent, {parents} returns empty.
|
|
|
|
// If it has two parents, {parents} returns revs and short hashes, not
|
|
|
|
// full hashes. Try to avoid making calls to "hg parents" because it's
|
|
|
|
// relatively expensive.
|
|
|
|
$commit_parents = null;
|
|
|
|
if (!$parents) {
|
|
|
|
if ($last_node) {
|
|
|
|
$commit_parents = array($last_node);
|
|
|
|
}
|
2012-02-28 16:59:40 -08:00
|
|
|
}
|
2011-09-26 14:35:42 -07:00
|
|
|
|
2012-05-11 13:52:14 -07:00
|
|
|
if (!$commit_parents) {
|
2012-05-09 15:56:16 -07:00
|
|
|
// We didn't get a cheap hit on previous commit, so do the full-cost
|
|
|
|
// "hg parents" call. We can run these in parallel, at least.
|
|
|
|
$futures[$node] = $this->execFutureLocal(
|
|
|
|
"parents --template='{node}\\n' --rev %s",
|
|
|
|
$node);
|
2012-02-28 16:59:40 -08:00
|
|
|
}
|
2012-05-09 15:56:16 -07:00
|
|
|
|
|
|
|
$commits[$node] = array(
|
|
|
|
'author' => $author,
|
|
|
|
'time' => strtotime($date),
|
|
|
|
'branch' => $branch,
|
|
|
|
'tag' => $tag,
|
|
|
|
'commit' => $node,
|
2012-05-11 06:07:33 -07:00
|
|
|
'rev' => $node, // TODO: Remove eventually.
|
2012-05-09 15:56:16 -07:00
|
|
|
'local' => $rev,
|
|
|
|
'parents' => $commit_parents,
|
|
|
|
'summary' => head(explode("\n", $desc)),
|
|
|
|
'message' => $desc,
|
|
|
|
);
|
|
|
|
|
|
|
|
$last_node = $node;
|
2011-09-26 14:35:42 -07:00
|
|
|
}
|
2012-05-09 15:56:16 -07:00
|
|
|
|
|
|
|
foreach (Futures($futures)->limit(4) as $node => $future) {
|
|
|
|
list($parents) = $future->resolvex();
|
|
|
|
$parents = array_filter(explode("\n", $parents));
|
|
|
|
$commits[$node]['parents'] = $parents;
|
|
|
|
}
|
|
|
|
|
Improve default fill of Differential messages from commits
Summary:
- When you run "arc diff" in Mercurial, we currently give you an empty template. Instead, prefill a likely template by parsing messages, as we do for Git.
- Unify Git and Mercurial logic for acquiring messages, since `getLocalCommitInformation()` now provides this information. This should improve Git performance, and allows us to delete some code.
- Merge messages more intelligently. Currently, we just overwrite fields. Instead, merge arrays (like ccs, reviewers, tasks) and concatenate strings (like summary and test plan). We need to special case "title", see inline.
- @csilvers: this is a precursor to implementing "--reviewers", etc.
Test Plan: See next post, test plan includes giant output.
Reviewers: btrahan, csilvers, Makinde, jungejason, vrana
Reviewed By: csilvers
CC: aran
Differential Revision: https://secure.phabricator.com/D2536
2012-05-22 11:08:57 -07:00
|
|
|
// Put commits in newest-first order, to be consistent with Git and the
|
|
|
|
// expected order of "hg log" and "git log" under normal circumstances.
|
|
|
|
// The order of ancestors() is oldest-first.
|
|
|
|
$commits = array_reverse($commits);
|
|
|
|
|
2012-05-09 15:56:16 -07:00
|
|
|
$this->localCommitInfo = $commits;
|
2011-09-26 14:35:42 -07:00
|
|
|
}
|
|
|
|
|
2012-02-28 16:59:40 -08:00
|
|
|
return $this->localCommitInfo;
|
2011-08-23 18:48:55 -07:00
|
|
|
}
|
|
|
|
|
2011-12-13 09:44:31 -08:00
|
|
|
public function getBlame($path) {
|
2012-02-28 16:56:57 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'annotate -u -v -c --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->getRelativeCommit(),
|
|
|
|
$path);
|
|
|
|
|
|
|
|
$blame = array();
|
|
|
|
foreach (explode("\n", trim($stdout)) as $line) {
|
|
|
|
if (!strlen($line)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$matches = null;
|
2011-12-12 14:28:34 -08:00
|
|
|
$ok = preg_match('/^\s*([^:]+?) [a-f0-9]{12}: (.*)$/', $line, $matches);
|
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
|
|
|
|
|
|
|
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();
|
2012-03-19 19:17:10 -07:00
|
|
|
|
|
|
|
if (!$diff) {
|
|
|
|
$this->status = array();
|
|
|
|
return $this->status;
|
|
|
|
}
|
|
|
|
|
2011-09-14 18:11:00 -07:00
|
|
|
$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
|
|
|
}
|
|
|
|
|
2012-02-28 16:56:57 -08:00
|
|
|
list($stdout) = $this->execxLocal('status');
|
2011-08-09 18:54:10 -07:00
|
|
|
|
2011-09-16 03:53:52 -07:00
|
|
|
$working_status = ArcanistMercurialParser::parseMercurialStatus($stdout);
|
2011-09-14 18:11:00 -07:00
|
|
|
foreach ($working_status as $path => $status) {
|
2011-11-01 17:10:48 -07:00
|
|
|
if ($status & ArcanistRepositoryAPI::FLAG_UNTRACKED) {
|
|
|
|
// If the file is untracked, don't mark it uncommitted.
|
|
|
|
continue;
|
|
|
|
}
|
2011-09-14 18:11:00 -07:00
|
|
|
$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();
|
|
|
|
|
2012-07-01 11:06:05 -07:00
|
|
|
// NOTE: In Mercurial, "--rev x" means "diff between x and the working
|
|
|
|
// copy state", while "--rev x..." means "diff between x and the working
|
|
|
|
// copy commit" (i.e., from 'x' to '.'). The latter excludes any dirty
|
|
|
|
// changes in the working copy.
|
|
|
|
|
|
|
|
$range = $this->getRelativeCommit();
|
|
|
|
if (!$this->includeDirectoryStateInDiffs) {
|
|
|
|
$range .= '...';
|
|
|
|
}
|
|
|
|
|
2012-02-28 16:56:57 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
2012-07-01 11:06:05 -07:00
|
|
|
'diff %C --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
|
|
|
$options,
|
2012-07-01 11:06:05 -07:00
|
|
|
$range,
|
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() {
|
2012-07-01 11:06:05 -07:00
|
|
|
return $this->getRawDiffText('');
|
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 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) {
|
2012-02-28 16:56:57 -08:00
|
|
|
list($err, $stdout) = $this->execManualLocal(
|
|
|
|
'cat --rev %s -- %s',
|
2011-11-01 17:21:39 -07:00
|
|
|
$revision,
|
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);
|
2011-11-01 17:21:39 -07:00
|
|
|
if ($err) {
|
|
|
|
// Assume this is "no file at revision", i.e. a deleted or added file.
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return $stdout;
|
|
|
|
}
|
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-12-02 16:21:14 -08:00
|
|
|
public function getWorkingCopyRevision() {
|
2012-07-01 11:06:05 -07:00
|
|
|
return '.';
|
2011-09-14 19:08:16 -07:00
|
|
|
}
|
|
|
|
|
2012-06-04 15:30:50 -04:00
|
|
|
public function isHistoryDefaultImmutable() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function supportsAmend() {
|
|
|
|
list ($err, $stdout) = $this->execManualLocal('help commit');
|
|
|
|
if ($err) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return (strstr($stdout, "amend") !== false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-14 18:44:54 -07:00
|
|
|
public function supportsRelativeLocalCommits() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-07 13:02:53 -08:00
|
|
|
public function hasLocalCommit($commit) {
|
2012-03-19 19:17:10 -07:00
|
|
|
try {
|
|
|
|
$this->getCanonicalRevisionName($commit);
|
|
|
|
return true;
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-07 13:02:53 -08:00
|
|
|
}
|
|
|
|
|
2012-07-01 11:06:05 -07:00
|
|
|
public function getCommitMessage($commit) {
|
|
|
|
list($message) = $this->execxLocal(
|
|
|
|
'log --template={desc} --rev %s',
|
|
|
|
$commit);
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
2011-09-14 18:44:54 -07:00
|
|
|
public function parseRelativeLocalCommit(array $argv) {
|
|
|
|
if (count($argv) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (count($argv) != 1) {
|
|
|
|
throw new ArcanistUsageException("Specify only one commit.");
|
|
|
|
}
|
2012-05-11 06:07:33 -07:00
|
|
|
|
2012-06-14 12:02:41 -07:00
|
|
|
$this->setBaseCommitExplanation("you explicitly specified it.");
|
2012-05-11 06:07:33 -07:00
|
|
|
|
2011-09-14 18:44:54 -07:00
|
|
|
// This does the "hg id" call we need to normalize/validate the revision
|
|
|
|
// identifier.
|
|
|
|
$this->setRelativeCommit(reset($argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAllLocalChanges() {
|
|
|
|
$diff = $this->getFullMercurialDiff();
|
2012-02-29 12:18:46 -08:00
|
|
|
if (!strlen(trim($diff))) {
|
|
|
|
return array();
|
|
|
|
}
|
2011-09-14 18:44:54 -07:00
|
|
|
$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(
|
2012-02-28 16:56:57 -08:00
|
|
|
'(cd %s && HGPLAIN=1 hg merge --rev %s && hg commit -m %s)',
|
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
|
|
|
$this->getPath(),
|
|
|
|
$branch,
|
|
|
|
$message);
|
|
|
|
} else {
|
|
|
|
$err = phutil_passthru(
|
2012-02-28 16:56:57 -08:00
|
|
|
'(cd %s && HGPLAIN=1 hg merge && hg commit -m %s)',
|
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
|
|
|
$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).";
|
|
|
|
}
|
|
|
|
|
2012-02-28 16:59:40 -08:00
|
|
|
public function getCommitMessageLog() {
|
|
|
|
list($stdout) = $this->execxLocal(
|
2012-07-01 11:06:05 -07:00
|
|
|
"log --template '{node}\\2{desc}\\1' --rev %s --branch %s --",
|
|
|
|
'ancestors(.) - ancestors('.$this->getRelativeCommit().')',
|
|
|
|
$this->getBranchName());
|
2012-02-28 16:59:40 -08:00
|
|
|
|
|
|
|
$map = array();
|
|
|
|
|
2012-07-01 11:06:05 -07:00
|
|
|
$logs = explode("\1", trim($stdout));
|
2012-02-28 16:59:40 -08:00
|
|
|
foreach (array_filter($logs) as $log) {
|
2012-07-01 11:06:05 -07:00
|
|
|
list($node, $desc) = explode("\2", $log);
|
2012-02-28 16:59:40 -08:00
|
|
|
$map[$node] = $desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_reverse($map);
|
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
public function loadWorkingCopyDifferentialRevisions(
|
|
|
|
ConduitClient $conduit,
|
|
|
|
array $query) {
|
|
|
|
|
2012-06-04 15:30:50 -04:00
|
|
|
$messages = $this->getCommitMessageLog();
|
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
|
|
|
|
// First, try to find revisions by explicit revision IDs in commit messages.
|
|
|
|
$reason_map = array();
|
|
|
|
$revision_ids = array();
|
|
|
|
foreach ($messages as $node_id => $message) {
|
|
|
|
$object = ArcanistDifferentialCommitMessage::newFromRawCorpus($message);
|
|
|
|
|
|
|
|
if ($object->getRevisionID()) {
|
|
|
|
$revision_ids[] = $object->getRevisionID();
|
|
|
|
$reason_map[$object->getRevisionID()] = $node_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($revision_ids) {
|
|
|
|
$results = $conduit->callMethodSynchronous(
|
|
|
|
'differential.query',
|
|
|
|
$query + array(
|
|
|
|
'ids' => $revision_ids,
|
|
|
|
));
|
|
|
|
|
|
|
|
foreach ($results as $key => $result) {
|
|
|
|
$hash = substr($reason_map[$result['id']], 0, 16);
|
|
|
|
$results[$key]['why'] =
|
|
|
|
"Commit message for '{$hash}' has explicit 'Differential Revision'.";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
// Try to find revisions by hash.
|
|
|
|
$hashes = array();
|
|
|
|
foreach ($this->getLocalCommitInformation() as $commit) {
|
2012-05-11 06:07:33 -07:00
|
|
|
$hashes[] = array('hgcm', $commit['commit']);
|
2012-01-24 08:07:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
$results = $conduit->callMethodSynchronous(
|
|
|
|
'differential.query',
|
|
|
|
$query + array(
|
|
|
|
'commitHashes' => $hashes,
|
|
|
|
));
|
|
|
|
|
2012-05-11 06:07:33 -07:00
|
|
|
foreach ($results as $key => $hash) {
|
|
|
|
$results[$key]['why'] =
|
|
|
|
"A mercurial commit hash in the commit range is already attached ".
|
|
|
|
"to the Differential revision.";
|
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
2012-03-16 13:40:33 -07:00
|
|
|
public function updateWorkingCopy() {
|
|
|
|
$this->execxLocal('up');
|
|
|
|
}
|
|
|
|
|
2012-06-04 15:30:50 -04:00
|
|
|
public function amendCommit($message) {
|
|
|
|
$tmp_file = new TempFile();
|
|
|
|
Filesystem::writeFile($tmp_file, $message);
|
|
|
|
$this->execxLocal(
|
|
|
|
'commit --amend -l %s',
|
|
|
|
$tmp_file);
|
|
|
|
}
|
|
|
|
|
2012-03-19 19:17:10 -07:00
|
|
|
public function setIncludeDirectoryStateInDiffs($include) {
|
|
|
|
$this->includeDirectoryStateInDiffs = $include;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-05-11 06:07:33 -07:00
|
|
|
public function getCommitSummary($commit) {
|
|
|
|
if ($commit == 'null') {
|
|
|
|
return '(The Empty Void)';
|
|
|
|
}
|
|
|
|
|
|
|
|
list($summary) = $this->execxLocal(
|
|
|
|
'log --template {desc} --limit 1 --rev %s',
|
|
|
|
$commit);
|
|
|
|
|
|
|
|
$summary = head(explode("\n", $summary));
|
|
|
|
|
|
|
|
return trim($summary);
|
|
|
|
}
|
|
|
|
|
2012-06-21 18:12:09 -07:00
|
|
|
public function resolveBaseCommitRule($rule, $source) {
|
|
|
|
list($type, $name) = explode(':', $rule, 2);
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
case 'hg':
|
|
|
|
$matches = null;
|
|
|
|
if (preg_match('/^gca\((.+)\)$/', $name, $matches)) {
|
|
|
|
list($err, $merge_base) = $this->execManualLocal(
|
|
|
|
'log --template={node} --rev %s',
|
|
|
|
sprintf('ancestor(., %s)', $matches[1]));
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"it is the greatest common ancestor of '{$matches[1]}' and ., as".
|
|
|
|
"specified by '{$rule}' in your {$source} 'base' ".
|
|
|
|
"configuration.");
|
|
|
|
return trim($merge_base);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
list($err) = $this->execManualLocal(
|
|
|
|
'id -r %s',
|
|
|
|
$name);
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"it is specified by '{$rule}' in your {$source} 'base' ".
|
|
|
|
"configuration.");
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'arc':
|
|
|
|
switch ($name) {
|
|
|
|
case 'empty':
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"you specified '{$rule}' in your {$source} 'base' ".
|
|
|
|
"configuration.");
|
|
|
|
return 'null';
|
|
|
|
case 'outgoing':
|
|
|
|
list($err, $outgoing_base) = $this->execManualLocal(
|
|
|
|
'log --template={node} --rev %s',
|
|
|
|
'limit(reverse(ancestors(.) - outgoing()), 1)'
|
|
|
|
);
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"it is the first ancestor of the working copy that is not ".
|
|
|
|
"outgoing, and it matched the rule {$rule} in your {$source} ".
|
|
|
|
"'base' configuration.");
|
|
|
|
return trim($outgoing_base);
|
|
|
|
}
|
2012-07-01 11:06:05 -07:00
|
|
|
case 'amended':
|
|
|
|
$text = $this->getCommitMessage('.');
|
|
|
|
$message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
|
|
|
|
$text);
|
|
|
|
if ($message->getRevisionID()) {
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"'.' has been amended with 'Differential Revision:', ".
|
|
|
|
"as specified by '{$rule}' in your {$source} 'base' ".
|
|
|
|
"configuration.");
|
|
|
|
// NOTE: This should be safe because Mercurial doesn't support
|
|
|
|
// amend until 2.2.
|
|
|
|
return '.^';
|
|
|
|
}
|
|
|
|
break;
|
[Arcanist] add an arc:bookmark base for Mercurial
Summary:
This adds a new base option that Does the Right Thing (or as close to
it as I can get) for someone using Mercurial bookmarks as lightweight
branches, and where each branch should be one differential revision.
Specifically, it walks backwards through the ancestors of the working
copy until it finds a revision that is either not outgoing (ie already
in the remote) or that is bookmarked. This means that bookmarks
effectively act as delimiters, and point at the last revision that
will be included in an arc diff.
Test Plan:
[14:40:54 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1226 $ hg log -r '(first(outgoing())^)::' --template='node: {node}\nbookmark: {bookmarks}\n\n' -G
@ node: c8379ef32b0d0e6cf94fe636751ea4fe1353e157
| bookmark:
|
| o node: 14f03139049cbda339190b814e52f4ec8b05c431
| | bookmark: more
| |
| o node: 6970e9263ab8c6da428420606d1f15c9980da183
| | bookmark: something
| |
| o node: 433a93023f03d5f3eddaa243fa973d32a1566aee
|/ bookmark:
|
o node: f47ccfe34267592dd2e336174a3a311b8783c024
| bookmark:
|
[14:41:00 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1226 $ ~/devtools/arcanist/bin/arc which --show-base --base 'arc:bookmark'
f47ccfe34267592dd2e336174a3a311b8783c024
[14:41:05 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1226 $ hg up 14f03139049cbda339190b814e52f4ec8b05c431
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
[14:41:10 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1226 $ ~/devtools/arcanist/bin/arc which --show-base --base 'arc:bookmark'
6970e9263ab8c6da428420606d1f15c9980da183
[14:41:14 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1226 $ hg up 6970e9263ab8c6da428420606d1f15c9980da183
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[14:41:44 Tue Jun 26 2012] dschleimer@dev4022.snc6 ~/hg-dummy
hg-dummy 1227 $ ~/devtools/arcanist/bin/arc which --show-base --base 'arc:bookmark'
f47ccfe34267592dd2e336174a3a311b8783c024
Reviewers: epriestley, bos
Reviewed By: epriestley
CC: aran, Korvin
Maniphest Tasks: T1331
Differential Revision: https://secure.phabricator.com/D2863
2012-06-26 15:04:35 -07:00
|
|
|
case 'bookmark':
|
|
|
|
$revset =
|
|
|
|
'limit('.
|
|
|
|
' sort('.
|
|
|
|
' (ancestors(.) and bookmark() - .) or'.
|
|
|
|
' (ancestors(.) - outgoing()), '.
|
|
|
|
' -rev),'.
|
|
|
|
'1)';
|
|
|
|
list($err, $bookmark_base) = $this->execManualLocal(
|
|
|
|
'log --template={node} --rev %s',
|
|
|
|
$revset);
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
|
|
|
"it is the first ancestor of . that either has a bookmark, or ".
|
|
|
|
"is already in the remote and it matched the rule {$rule} in ".
|
|
|
|
"your {$source} 'base' configuration");
|
|
|
|
return trim($bookmark_base);
|
|
|
|
}
|
2012-06-21 18:12:09 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-06-30 16:00:48 -07:00
|
|
|
public function getSubversionInfo() {
|
|
|
|
$info = array();
|
|
|
|
$base_path = null;
|
|
|
|
$revision = null;
|
|
|
|
list($err, $raw_info) = $this->execManualLocal('svn info');
|
|
|
|
if (!$err) {
|
|
|
|
foreach (explode("\n", trim($raw_info)) as $line) {
|
|
|
|
list($key, $value) = explode(': ', $line, 2);
|
|
|
|
switch ($key) {
|
|
|
|
case 'URL':
|
|
|
|
$info['base_path'] = $value;
|
|
|
|
$base_path = $value;
|
|
|
|
break;
|
|
|
|
case 'Repository UUID':
|
|
|
|
$info['uuid'] = $value;
|
|
|
|
break;
|
|
|
|
case 'Revision':
|
|
|
|
$revision = $value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($base_path && $revision) {
|
|
|
|
$info['base_revision'] = $base_path.'@'.$revision;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $info;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getActiveBookmark() {
|
|
|
|
list($raw_output) = $this->execxLocal('bookmarks');
|
|
|
|
$raw_output = trim($raw_output);
|
|
|
|
if ($raw_output !== 'no bookmarks set') {
|
|
|
|
foreach (explode("\n", $raw_output) as $line) {
|
|
|
|
$line = trim($line);
|
|
|
|
if ('*' === $line[0]) {
|
|
|
|
return idx(explode(' ', $line, 3), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|