Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15302757
D9369.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
D9369.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D9369: --range support for git
Attached
Detach File
Event Timeline
Log In to Comment