diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -104,6 +104,8 @@ 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCommentStyleXHPASTLinterRuleTestCase.php', 'ArcanistCommitRef' => 'ref/ArcanistCommitRef.php', 'ArcanistCommitRefInspector' => 'inspector/ArcanistCommitRefInspector.php', + 'ArcanistCommitSymbolRef' => 'ref/commitsymbol/ArcanistCommitSymbolRef.php', + 'ArcanistCommitSymbolRefInspector' => 'ref/commitsymbol/ArcanistCommitSymbolRefInspector.php', 'ArcanistCommitUpstreamHardpointQuery' => 'query/ArcanistCommitUpstreamHardpointQuery.php', 'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php', 'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php', @@ -206,6 +208,7 @@ 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php', 'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php', 'ArcanistGitCommitMessageHardpointQuery' => 'query/ArcanistGitCommitMessageHardpointQuery.php', + 'ArcanistGitCommitSymbolCommitHardpointQuery' => 'ref/commitsymbol/ArcanistGitCommitSymbolCommitHardpointQuery.php', 'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php', 'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php', 'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php', @@ -1066,6 +1069,8 @@ 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistCommitRef' => 'ArcanistRef', 'ArcanistCommitRefInspector' => 'ArcanistRefInspector', + 'ArcanistCommitSymbolRef' => 'ArcanistRef', + 'ArcanistCommitSymbolRefInspector' => 'ArcanistRefInspector', 'ArcanistCommitUpstreamHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistCommitWorkflow' => 'ArcanistWorkflow', 'ArcanistCompilerLintRenderer' => 'ArcanistLintRenderer', @@ -1168,6 +1173,7 @@ 'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistGitAPI' => 'ArcanistRepositoryAPI', 'ArcanistGitCommitMessageHardpointQuery' => 'ArcanistWorkflowGitHardpointQuery', + 'ArcanistGitCommitSymbolCommitHardpointQuery' => 'ArcanistWorkflowGitHardpointQuery', 'ArcanistGitLandEngine' => 'ArcanistLandEngine', 'ArcanistGitUpstreamPath' => 'Phobject', 'ArcanistGitWorkingCopy' => 'ArcanistWorkingCopy', diff --git a/src/ref/commitsymbol/ArcanistCommitSymbolRef.php b/src/ref/commitsymbol/ArcanistCommitSymbolRef.php new file mode 100644 --- /dev/null +++ b/src/ref/commitsymbol/ArcanistCommitSymbolRef.php @@ -0,0 +1,37 @@ +getSymbol()); + } + + protected function newHardpoints() { + return array( + $this->newHardpoint(self::HARDPOINT_COMMIT), + ); + } + + public function setSymbol($symbol) { + $this->symbol = $symbol; + return $this; + } + + public function getSymbol() { + return $this->symbol; + } + + public function attachCommit(ArcanistCommitRef $commit) { + return $this->attachHardpoint(self::HARDPOINT_COMMIT, $commit); + } + + public function getCommit() { + return $this->getHardpoint(self::HARDPOINT_COMMIT); + } + +} diff --git a/src/ref/commitsymbol/ArcanistCommitSymbolRefInspector.php b/src/ref/commitsymbol/ArcanistCommitSymbolRefInspector.php new file mode 100644 --- /dev/null +++ b/src/ref/commitsymbol/ArcanistCommitSymbolRefInspector.php @@ -0,0 +1,22 @@ +setSymbol($argv[0]); + } + +} diff --git a/src/ref/commitsymbol/ArcanistGitCommitSymbolCommitHardpointQuery.php b/src/ref/commitsymbol/ArcanistGitCommitSymbolCommitHardpointQuery.php new file mode 100644 --- /dev/null +++ b/src/ref/commitsymbol/ArcanistGitCommitSymbolCommitHardpointQuery.php @@ -0,0 +1,92 @@ + $ref) { + $symbol_map[$key] = $ref->getSymbol(); + } + + $symbol_set = array_fuse($symbol_map); + foreach ($symbol_set as $symbol) { + $this->validateSymbol($symbol); + } + + $api = $this->getRepositoryAPI(); + + $symbol_list = implode("\n", $symbol_set); + + $future = $api->newFuture('cat-file --batch-check --') + ->write($symbol_list); + + list($stdout) = (yield $this->yieldFuture($future)); + + $lines = phutil_split_lines($stdout, $retain_endings = false); + + if (count($lines) !== count($symbol_set)) { + throw new Exception( + pht( + 'Execution of "git cat-file --batch-check" emitted an unexpected '. + 'number of lines, expected %s but got %s.', + phutil_count($symbol_set), + phutil_count($lines))); + } + + $hash_map = array(); + + $pairs = array_combine($symbol_set, $lines); + foreach ($pairs as $symbol => $line) { + $parts = explode(' ', $line, 3); + + if (count($parts) < 2) { + throw new Exception( + pht( + 'Execution of "git cat-file --batch-check" emitted an '. + 'unexpected line ("%s").', + $line)); + } + + list($hash, $type) = $parts; + + // NOTE: For now, symbols which map to tags (which, in turn, map to + // commits) are ignored here. + + if ($type !== 'commit') { + $hash_map[$symbol] = null; + continue; + } + + $hash_map[$symbol] = $hash; + } + + $results = array(); + foreach ($symbol_map as $key => $symbol) { + $results[$key] = $hash_map[$symbol]; + } + + yield $this->yieldMap($results); + } + + private function validateSymbol($symbol) { + if (strpos($symbol, "\n") !== false) { + throw new Exception( + pht( + 'Commit symbol "%s" contains a newline. This is not a valid '. + 'character in a Git commit symbol.', + addcslashes($symbol, "\\\n"))); + } + } + +} 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 @@ -673,6 +673,36 @@ return new ArcanistBranchRef(); } + final public function getCurrentCommitRef() { + $commit_hash = $this->newCurrentCommitHash(); + return $this->newCommitRef() + ->setCommitHash($commit_hash); + } + + protected function newCurrentCommitRef() { + $commit_hash = $this->newCurrentCommitHash(); + return $this->newCommitRefForSymbol($commit_hash); + } + + protected function newCommitRefForSymbol() { + throw new ArcanistCapabilityNotSupportedException($this); + } + + protected function newCurrentCommitHash() { + throw new ArcanistCapabilityNotSupportedException($this); + } + + final public function getCurrentWorkingCopyStateRef() { + return $this->newCurrentWorkingCopyStateRef(); + } + + protected function newCurrentWorkingCopyStateRef() { + $commit_ref = $this->getCurrentCommitRef(); + + return id(new ArcanistWorkingCopyStateRef()) + ->setCommitRef($commit_ref); + } + final public function newFuture($pattern /* , ... */) { $args = func_get_args(); return $this->buildLocalFuture($args)