Page MenuHomePhabricator

D9369.diff
No OneTemporary

D9369.diff

diff --git a/src/parser/__tests__/ArcanistBundleTestCase.php b/src/parser/__tests__/ArcanistBundleTestCase.php
--- a/src/parser/__tests__/ArcanistBundleTestCase.php
+++ b/src/parser/__tests__/ArcanistBundleTestCase.php
@@ -84,7 +84,9 @@
$configuration_manager);
$repository_api->setBaseCommitArgumentRules('arc:this');
- $diff = $repository_api->getFullGitDiff();
+ $diff = $repository_api->getFullGitDiff(
+ $repository_api->getBaseCommit(),
+ $repository_api->getHeadCommit());
$parser = new ArcanistDiffParser();
$parser->setRepositoryAPI($repository_api);
diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php
--- a/src/repository/api/ArcanistGitAPI.php
+++ b/src/repository/api/ArcanistGitAPI.php
@@ -16,6 +16,9 @@
*/
const GIT_MAGIC_ROOT_COMMIT = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+ private $symbolicHeadCommit = 'HEAD';
+ private $resolvedHeadCommit;
+
public static function newHookAPI($root) {
return new ArcanistGitAPI($root);
}
@@ -75,6 +78,21 @@
return !$this->repositoryHasNoCommits;
}
+ /**
+ * Tests if a child commit is descendant of a parent commit.
+ * If child and parent are the same, it returns false.
+ * @param child commit SHA.
+ * @param parent commit SHA.
+ * @return bool
+ */
+ private function isDescendant($child, $parent) {
+ list($common_ancestor) =
+ $this->execxLocal('merge-base %s %s', $child, $parent);
+ $common_ancestor = trim($common_ancestor);
+
+ return $common_ancestor == $parent && $common_ancestor != $child;
+ }
+
public function getLocalCommitInformation() {
if ($this->repositoryHasNoCommits) {
// Zero commits.
@@ -106,7 +124,16 @@
// this as being the commits X and Y. If we log "B..Y", we only show
// Y. With "Y --not B", we show X and Y.
- $against = csprintf('%s --not %s', 'HEAD', $this->getBaseCommit());
+ $base_commit = $this->getBaseCommit();
+ $head_commit = $this->getHeadCommit();
+ if ($this->isDescendant($head_commit, $base_commit) === false) {
+ throw new ArcanistUsageException(
+ "base commit ${base_commit} is not a child of head commit ".
+ "${head_commit}");
+ }
+
+ $against = csprintf('%s --not %s',
+ $this->getHeadCommit(), $this->getBaseCommit());
}
// NOTE: Windows escaping of "%" symbols apparently is inherently broken;
@@ -161,8 +188,9 @@
}
list($err, $merge_base) = $this->execManualLocal(
- 'merge-base %s HEAD',
- $symbolic_commit);
+ 'merge-base %s %s',
+ $symbolic_commit,
+ $this->getHeadCommit());
if ($err) {
throw new ArcanistUsageException(
"Unable to find any git commit named '{$symbolic_commit}' in ".
@@ -170,8 +198,8 @@
}
$this->setBaseCommitExplanation(
- "it is the merge-base of '{$symbolic_commit}' and HEAD, as you ".
- "explicitly specified.");
+ "it is the merge-base of '{$symbolic_commit}' and ".
+ "{$this->symbolicHeadCommit}, as you explicitly specified.");
return trim($merge_base);
}
@@ -301,6 +329,40 @@
return trim($merge_base);
}
+ public function getHeadCommit() {
+ if ($this->resolvedHeadCommit === null) {
+ $this->resolvedHeadCommit =
+ $this->resolveCommit($this->symbolicHeadCommit);
+ }
+
+ return $this->resolvedHeadCommit;
+ }
+
+ final public function setHeadCommit($symbolic_commit) {
+ $this->symbolicHeadCommit = $symbolic_commit;
+ $this->reloadCommitRange();
+ return $this;
+ }
+
+ /**
+ * Translates a symbolic commit (like "HEAD^") to a commit identifier.
+ * @param string_symbol commit.
+ * @return string the commit SHA.
+ */
+ private function resolveCommit($symbolic_commit) {
+ list($err, $commit_hash) = $this->execManualLocal(
+ 'rev-parse %s',
+ $symbolic_commit);
+
+ if ($err) {
+ throw new ArcanistUsageException(
+ "Unable to find any git commit named '{$symbolic_commit}' in ".
+ "this repository.");
+ }
+
+ return trim($commit_hash);
+ }
+
private function getDiffFullOptions($detect_moves_and_renames = true) {
$options = array(
self::getDiffBaseOptions(),
@@ -332,11 +394,22 @@
return implode(' ', $options);
}
- public function getFullGitDiff() {
+ /**
+ * @param the base revision
+ * @param head revision. If this is null, the generated diff will include the
+ * working copy
+ */
+ public function getFullGitDiff($base, $head=null) {
$options = $this->getDiffFullOptions();
+
+ $diff_revision = $base;
+ if ($head) {
+ $diff_revision .= '..'.$head;
+ }
+
list($stdout) = $this->execxLocal(
"diff {$options} %s --",
- $this->getBaseCommit());
+ $diff_revision);
return $stdout;
}
@@ -401,8 +474,9 @@
} else {
// 2..N commits.
list($stdout) = $this->execxLocal(
- 'log --first-parent --format=medium %s..HEAD',
- $this->getBaseCommit());
+ 'log --first-parent --format=medium %s..%s',
+ $this->getBaseCommit(),
+ $this->getHeadCommit());
}
return $stdout;
}
@@ -821,7 +895,7 @@
}
public function getAllLocalChanges() {
- $diff = $this->getFullGitDiff();
+ $diff = $this->getFullGitDiff($this->getBaseCommit());
if (!strlen(trim($diff))) {
return array();
}
diff --git a/src/repository/api/ArcanistRepositoryAPI.php b/src/repository/api/ArcanistRepositoryAPI.php
--- a/src/repository/api/ArcanistRepositoryAPI.php
+++ b/src/repository/api/ArcanistRepositoryAPI.php
@@ -575,6 +575,10 @@
return $this;
}
+ public function setHeadCommit($symbolic_commit) {
+ throw new ArcanistCapabilityNotSupportedException($this);
+ }
+
final public function getBaseCommit() {
if (!$this->supportsCommitRanges()) {
throw new ArcanistCapabilityNotSupportedException($this);
@@ -588,6 +592,10 @@
return $this->resolvedBaseCommit;
}
+ public function getHeadCommit() {
+ throw new ArcanistCapabilityNotSupportedException($this);
+ }
+
final public function reloadCommitRange() {
$this->resolvedBaseCommit = null;
$this->baseCommitExplanation = null;
diff --git a/src/workflow/ArcanistDiffWorkflow.php b/src/workflow/ArcanistDiffWorkflow.php
--- a/src/workflow/ArcanistDiffWorkflow.php
+++ b/src/workflow/ArcanistDiffWorkflow.php
@@ -395,6 +395,18 @@
),
),
'*' => 'paths',
+ 'head' => array(
+ 'param' => 'commit',
+ 'help' => "specify the head commit.\n".
+ "This disables many Arcanist/Phabricator features which depend on ".
+ "having access to the working copy.",
+ 'supports' => array('git'),
+ 'conflicts' => array(
+ 'lintall' => '--head suppresses lint.',
+ 'advice' => '--head suppresses lint.',
+ ),
+
+ )
);
if (phutil_is_windows()) {
@@ -433,8 +445,19 @@
array_unshift($argv, '--ansi');
}
- if ($this->getRepositoryAPI()->supportsCommitRanges()) {
- $this->getRepositoryAPI()->getBaseCommit();
+ $repo = $this->getRepositoryAPI();
+ $head_commit = $this->getArgument('head');
+ $range_supported = $repo->supportsCommitRanges();
+ if ($head_commit) {
+ if (!$range_supported) {
+ throw new ArcanistUsageException('Ranged are not supported');
+ }
+
+ $repo->setHeadCommit($head_commit);
+ }
+
+ if ($range_supported) {
+ $repo->getBaseCommit();
}
$script = phutil_get_library_root('arcanist').'/../scripts/arcanist.php';
@@ -661,7 +684,9 @@
if ($repository_api instanceof ArcanistSubversionAPI) {
$repository_api->limitStatusToPaths($this->getArgument('paths'));
}
- $this->requireCleanWorkingCopy();
+ if (!$this->getArgument('head')) {
+ $this->requireCleanWorkingCopy();
+ }
} catch (ArcanistUncommittedChangesException $ex) {
if ($repository_api instanceof ArcanistMercurialAPI) {
$use_dirty_changes = false;
@@ -937,7 +962,9 @@
$repository_api,
$paths);
} else if ($repository_api instanceof ArcanistGitAPI) {
- $diff = $repository_api->getFullGitDiff();
+ $diff = $repository_api->getFullGitDiff(
+ $repository_api->getBaseCommit(),
+ $repository_api->getHeadCommit());
if (!strlen($diff)) {
throw new ArcanistUsageException(
'No changes found. (Did you specify the wrong commit range?)');
@@ -1216,7 +1243,8 @@
private function runLint() {
if ($this->getArgument('nolint') ||
$this->getArgument('only') ||
- $this->isRawDiffSource()) {
+ $this->isRawDiffSource() ||
+ $this->getArgument('head')) {
return ArcanistLintWorkflow::RESULT_SKIP;
}
@@ -1297,7 +1325,8 @@
private function runUnit() {
if ($this->getArgument('nounit') ||
$this->getArgument('only') ||
- $this->isRawDiffSource()) {
+ $this->isRawDiffSource() ||
+ $this->getArgument('head')) {
return ArcanistUnitWorkflow::RESULT_SKIP;
}
diff --git a/src/workflow/ArcanistExportWorkflow.php b/src/workflow/ArcanistExportWorkflow.php
--- a/src/workflow/ArcanistExportWorkflow.php
+++ b/src/workflow/ArcanistExportWorkflow.php
@@ -178,7 +178,9 @@
if ($repository_api instanceof ArcanistGitAPI) {
$this->parseBaseCommitArgument($this->getArgument('paths'));
- $diff = $repository_api->getFullGitDiff();
+ $diff = $repository_api->getFullGitDiff(
+ $repository_api->getBaseCommit(),
+ $repository_api->getHeadCommit());
$changes = $parser->parseDiff($diff);
$authors = $this->getConduit()->callMethodSynchronous(
'user.query',
diff --git a/src/workflow/ArcanistWhichWorkflow.php b/src/workflow/ArcanistWhichWorkflow.php
--- a/src/workflow/ArcanistWhichWorkflow.php
+++ b/src/workflow/ArcanistWhichWorkflow.php
@@ -61,6 +61,10 @@
),
'supports' => array('git', 'hg'),
),
+ 'head' => array(
+ 'param' => 'commit',
+ 'help' => 'specify the head commit.'
+ ),
'*' => 'commit',
);
}
@@ -83,7 +87,19 @@
$repository_api->setBaseCommitArgumentRules(
$this->getArgument('base', ''));
- if ($repository_api->supportsCommitRanges()) {
+ $supports_ranges = $repository_api->supportsCommitRanges();
+
+ if ($this->getArgument('head')) {
+ if ($supports_ranges === false) {
+ throw new Exception('--head is not supported in this VCS');
+ }
+
+ $head_commit = $this->getArgument('head');
+ $arg .= " --head {$head_commit}";
+ $repository_api->setHeadCommit($head_commit);
+ }
+
+ if ($supports_ranges) {
$relative = $repository_api->getBaseCommit();
if ($this->getArgument('show-base')) {
@@ -111,7 +127,8 @@
$relative = substr($relative, 0, 16);
if ($repository_api instanceof ArcanistGitAPI) {
- $command = "git diff {$relative}..HEAD";
+ $head = $this->getArgument('head', 'HEAD');
+ $command = "git diff {$relative}..{$head}";
} else if ($repository_api instanceof ArcanistMercurialAPI) {
$command = "hg diff --rev {$relative}";
} else {

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 6, 9:05 PM (5 d, 11 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7309154
Default Alt Text
D9369.diff (11 KB)

Event Timeline