mirror of
https://we.phorge.it/source/arcanist.git
synced 2025-01-09 14:21:01 +01:00
(stable) Promote 2021 Week 4
This commit is contained in:
commit
66d1acec70
22 changed files with 212 additions and 68 deletions
|
@ -762,6 +762,7 @@ phutil_register_library_map(array(
|
||||||
'PhutilGitHubResponse' => 'future/github/PhutilGitHubResponse.php',
|
'PhutilGitHubResponse' => 'future/github/PhutilGitHubResponse.php',
|
||||||
'PhutilGitURI' => 'parser/PhutilGitURI.php',
|
'PhutilGitURI' => 'parser/PhutilGitURI.php',
|
||||||
'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php',
|
'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php',
|
||||||
|
'PhutilGitsprintfTestCase' => 'xsprintf/__tests__/PhutilGitsprintfTestCase.php',
|
||||||
'PhutilHTMLParser' => 'parser/html/PhutilHTMLParser.php',
|
'PhutilHTMLParser' => 'parser/html/PhutilHTMLParser.php',
|
||||||
'PhutilHTMLParserTestCase' => 'parser/html/__tests__/PhutilHTMLParserTestCase.php',
|
'PhutilHTMLParserTestCase' => 'parser/html/__tests__/PhutilHTMLParserTestCase.php',
|
||||||
'PhutilHTTPEngineExtension' => 'future/http/PhutilHTTPEngineExtension.php',
|
'PhutilHTTPEngineExtension' => 'future/http/PhutilHTTPEngineExtension.php',
|
||||||
|
@ -927,6 +928,7 @@ phutil_register_library_map(array(
|
||||||
'csprintf' => 'xsprintf/csprintf.php',
|
'csprintf' => 'xsprintf/csprintf.php',
|
||||||
'exec_manual' => 'future/exec/execx.php',
|
'exec_manual' => 'future/exec/execx.php',
|
||||||
'execx' => 'future/exec/execx.php',
|
'execx' => 'future/exec/execx.php',
|
||||||
|
'gitsprintf' => 'xsprintf/gitsprintf.php',
|
||||||
'head' => 'utils/utils.php',
|
'head' => 'utils/utils.php',
|
||||||
'head_key' => 'utils/utils.php',
|
'head_key' => 'utils/utils.php',
|
||||||
'hgsprintf' => 'xsprintf/hgsprintf.php',
|
'hgsprintf' => 'xsprintf/hgsprintf.php',
|
||||||
|
@ -1043,6 +1045,7 @@ phutil_register_library_map(array(
|
||||||
'xsprintf' => 'xsprintf/xsprintf.php',
|
'xsprintf' => 'xsprintf/xsprintf.php',
|
||||||
'xsprintf_callback_example' => 'xsprintf/xsprintf.php',
|
'xsprintf_callback_example' => 'xsprintf/xsprintf.php',
|
||||||
'xsprintf_command' => 'xsprintf/csprintf.php',
|
'xsprintf_command' => 'xsprintf/csprintf.php',
|
||||||
|
'xsprintf_git' => 'xsprintf/gitsprintf.php',
|
||||||
'xsprintf_javascript' => 'xsprintf/jsprintf.php',
|
'xsprintf_javascript' => 'xsprintf/jsprintf.php',
|
||||||
'xsprintf_ldap' => 'xsprintf/ldapsprintf.php',
|
'xsprintf_ldap' => 'xsprintf/ldapsprintf.php',
|
||||||
'xsprintf_mercurial' => 'xsprintf/hgsprintf.php',
|
'xsprintf_mercurial' => 'xsprintf/hgsprintf.php',
|
||||||
|
@ -1825,6 +1828,7 @@ phutil_register_library_map(array(
|
||||||
'PhutilGitHubResponse' => 'Phobject',
|
'PhutilGitHubResponse' => 'Phobject',
|
||||||
'PhutilGitURI' => 'Phobject',
|
'PhutilGitURI' => 'Phobject',
|
||||||
'PhutilGitURITestCase' => 'PhutilTestCase',
|
'PhutilGitURITestCase' => 'PhutilTestCase',
|
||||||
|
'PhutilGitsprintfTestCase' => 'PhutilTestCase',
|
||||||
'PhutilHTMLParser' => 'Phobject',
|
'PhutilHTMLParser' => 'Phobject',
|
||||||
'PhutilHTMLParserTestCase' => 'PhutilTestCase',
|
'PhutilHTMLParserTestCase' => 'PhutilTestCase',
|
||||||
'PhutilHTTPEngineExtension' => 'Phobject',
|
'PhutilHTTPEngineExtension' => 'Phobject',
|
||||||
|
|
|
@ -180,10 +180,10 @@ final class PhutilErrorHandler extends Phobject {
|
||||||
* @return void
|
* @return void
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
public static function handleError($num, $str, $file, $line, $ctx) {
|
public static function handleError($num, $str, $file, $line, $ctx = null) {
|
||||||
|
|
||||||
foreach (self::$traps as $trap) {
|
foreach (self::$traps as $trap) {
|
||||||
$trap->addError($num, $str, $file, $line, $ctx);
|
$trap->addError($num, $str, $file, $line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error_reporting() & $num) == 0) {
|
if ((error_reporting() & $num) == 0) {
|
||||||
|
@ -214,7 +214,6 @@ final class PhutilErrorHandler extends Phobject {
|
||||||
array(
|
array(
|
||||||
'file' => $file,
|
'file' => $file,
|
||||||
'line' => $line,
|
'line' => $line,
|
||||||
'context' => $ctx,
|
|
||||||
'error_code' => $num,
|
'error_code' => $num,
|
||||||
'trace' => $trace,
|
'trace' => $trace,
|
||||||
));
|
));
|
||||||
|
|
|
@ -41,13 +41,12 @@ final class PhutilErrorTrap extends Phobject {
|
||||||
private $destroyed;
|
private $destroyed;
|
||||||
private $errors = array();
|
private $errors = array();
|
||||||
|
|
||||||
public function addError($num, $str, $file, $line, $ctx) {
|
public function addError($num, $str, $file, $line) {
|
||||||
$this->errors[] = array(
|
$this->errors[] = array(
|
||||||
'num' => $num,
|
'num' => $num,
|
||||||
'str' => $str,
|
'str' => $str,
|
||||||
'file' => $file,
|
'file' => $file,
|
||||||
'line' => $line,
|
'line' => $line,
|
||||||
'ctx' => $ctx,
|
|
||||||
);
|
);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,7 +268,8 @@ final class PhutilOAuth1Future extends FutureProxy {
|
||||||
throw new Exception(pht('%s failed!', 'openssl_sign()'));
|
throw new Exception(pht('%s failed!', 'openssl_sign()'));
|
||||||
}
|
}
|
||||||
|
|
||||||
openssl_free_key($pkey);
|
// Deprecated in PHP 8; key is automatically freed.
|
||||||
|
@openssl_free_key($pkey);
|
||||||
|
|
||||||
return base64_encode($signature);
|
return base64_encode($signature);
|
||||||
case 'PLAINTEXT':
|
case 'PLAINTEXT':
|
||||||
|
|
|
@ -274,9 +274,11 @@ final class ArcanistGitLandEngine
|
||||||
// as changes.
|
// as changes.
|
||||||
|
|
||||||
list($changes) = $api->execxLocal(
|
list($changes) = $api->execxLocal(
|
||||||
'diff --no-ext-diff %s..%s --',
|
'diff --no-ext-diff %s --',
|
||||||
|
gitsprintf(
|
||||||
|
'%s..%s',
|
||||||
$into_commit,
|
$into_commit,
|
||||||
$max_hash);
|
$max_hash));
|
||||||
$changes = trim($changes);
|
$changes = trim($changes);
|
||||||
if (!strlen($changes)) {
|
if (!strlen($changes)) {
|
||||||
|
|
||||||
|
@ -762,7 +764,7 @@ final class ArcanistGitLandEngine
|
||||||
$api = $this->getRepositoryAPI();
|
$api = $this->getRepositoryAPI();
|
||||||
|
|
||||||
list($stdout) = $api->execxLocal(
|
list($stdout) = $api->execxLocal(
|
||||||
'merge-base %s %s',
|
'merge-base -- %s %s',
|
||||||
$branch,
|
$branch,
|
||||||
$commit);
|
$commit);
|
||||||
$merge_base = trim($stdout);
|
$merge_base = trim($stdout);
|
||||||
|
@ -781,7 +783,7 @@ final class ArcanistGitLandEngine
|
||||||
list($info) = $api->execxLocal(
|
list($info) = $api->execxLocal(
|
||||||
'log -n1 --format=%s %s --',
|
'log -n1 --format=%s %s --',
|
||||||
'%aD%n%an%n%ae',
|
'%aD%n%an%n%ae',
|
||||||
$commit);
|
gitsprintf('%s', $commit));
|
||||||
|
|
||||||
$info = trim($info);
|
$info = trim($info);
|
||||||
list($date, $author, $email) = explode("\n", $info, 3);
|
list($date, $author, $email) = explode("\n", $info, 3);
|
||||||
|
@ -1452,19 +1454,19 @@ final class ArcanistGitLandEngine
|
||||||
$commit_map = array();
|
$commit_map = array();
|
||||||
foreach ($symbols as $symbol) {
|
foreach ($symbols as $symbol) {
|
||||||
$symbol_commit = $symbol->getCommit();
|
$symbol_commit = $symbol->getCommit();
|
||||||
$format = '%H%x00%P%x00%s%x00';
|
$format = '--format=%H%x00%P%x00%s%x00';
|
||||||
|
|
||||||
if ($into_commit === null) {
|
if ($into_commit === null) {
|
||||||
list($commits) = $api->execxLocal(
|
list($commits) = $api->execxLocal(
|
||||||
'log %s --format=%s',
|
'log %s %s --',
|
||||||
$symbol_commit,
|
$format,
|
||||||
$format);
|
gitsprintf('%s', $symbol_commit));
|
||||||
} else {
|
} else {
|
||||||
list($commits) = $api->execxLocal(
|
list($commits) = $api->execxLocal(
|
||||||
'log %s --not %s --format=%s',
|
'log %s %s --not %s --',
|
||||||
$symbol_commit,
|
$format,
|
||||||
$into_commit,
|
gitsprintf('%s', $symbol_commit),
|
||||||
$format);
|
gitsprintf('%s', $into_commit));
|
||||||
}
|
}
|
||||||
|
|
||||||
$commits = phutil_split_lines($commits, false);
|
$commits = phutil_split_lines($commits, false);
|
||||||
|
|
|
@ -274,7 +274,7 @@ abstract class ArcanistLintEngine extends Phobject {
|
||||||
return ArcanistLintSeverity::isAtLeastAsSevere($severity, $minimum);
|
return ArcanistLintSeverity::isAtLeastAsSevere($severity, $minimum);
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function shouldUseCache(
|
private function shouldUseCache(
|
||||||
$cache_granularity,
|
$cache_granularity,
|
||||||
$repository_version) {
|
$repository_version) {
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ abstract class ArcanistLintEngine extends Phobject {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function isRelevantMessage(ArcanistLintMessage $message) {
|
private function isRelevantMessage(ArcanistLintMessage $message) {
|
||||||
// When a user runs "arc lint", we default to raising only warnings on
|
// When a user runs "arc lint", we default to raising only warnings on
|
||||||
// lines they have changed (errors are still raised anywhere in the
|
// lines they have changed (errors are still raised anywhere in the
|
||||||
// file). The list of $changed lines may be null, to indicate that the
|
// file). The list of $changed lines may be null, to indicate that the
|
||||||
|
|
|
@ -291,7 +291,7 @@ abstract class ArcanistLinter extends Phobject {
|
||||||
* @param list<string>
|
* @param list<string>
|
||||||
* @return list<string>
|
* @return list<string>
|
||||||
*/
|
*/
|
||||||
final private function filterPaths(array $paths) {
|
private function filterPaths(array $paths) {
|
||||||
$engine = $this->getEngine();
|
$engine = $this->getEngine();
|
||||||
|
|
||||||
$keep = array();
|
$keep = array();
|
||||||
|
|
|
@ -35,7 +35,8 @@ final class ArcanistPyFlakesLinter extends ArcanistExternalLinter {
|
||||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||||
|
|
||||||
$matches = array();
|
$matches = array();
|
||||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
$pattern = '/^(?P<version>\d+\.\d+\.\d+)( Python.*)?$/';
|
||||||
|
if (preg_match($pattern, $stdout, $matches)) {
|
||||||
return $matches['version'];
|
return $matches['version'];
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -52,7 +53,8 @@ final class ArcanistPyFlakesLinter extends ArcanistExternalLinter {
|
||||||
$messages = array();
|
$messages = array();
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$matches = null;
|
$matches = null;
|
||||||
if (!preg_match('/^(.*?):(\d+): (.*)$/', $line, $matches)) {
|
$pattern = '/^(?<path>.*?):(?<line>\d+):(?<column>\d*) (?<message>.*)$/';
|
||||||
|
if (!preg_match($pattern, $line, $matches)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($matches as $key => $match) {
|
foreach ($matches as $key => $match) {
|
||||||
|
@ -60,7 +62,7 @@ final class ArcanistPyFlakesLinter extends ArcanistExternalLinter {
|
||||||
}
|
}
|
||||||
|
|
||||||
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
|
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
|
||||||
$description = $matches[3];
|
$description = $matches['message'];
|
||||||
|
|
||||||
$error_regexp = '/(^undefined|^duplicate|before assignment$)/';
|
$error_regexp = '/(^undefined|^duplicate|before assignment$)/';
|
||||||
if (preg_match($error_regexp, $description)) {
|
if (preg_match($error_regexp, $description)) {
|
||||||
|
@ -69,7 +71,10 @@ final class ArcanistPyFlakesLinter extends ArcanistExternalLinter {
|
||||||
|
|
||||||
$message = new ArcanistLintMessage();
|
$message = new ArcanistLintMessage();
|
||||||
$message->setPath($path);
|
$message->setPath($path);
|
||||||
$message->setLine($matches[2]);
|
$message->setLine($matches['line']);
|
||||||
|
if ($matches['column'] != '') {
|
||||||
|
$message->setChar($matches['column']);
|
||||||
|
}
|
||||||
$message->setCode($this->getLinterName());
|
$message->setCode($this->getLinterName());
|
||||||
$message->setName($this->getLinterName());
|
$message->setName($this->getLinterName());
|
||||||
$message->setDescription($description);
|
$message->setDescription($description);
|
||||||
|
|
|
@ -3,4 +3,4 @@ if (foo = 'bar') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:2:16:W084
|
warning:2:9:W084
|
||||||
|
|
|
@ -2,6 +2,6 @@ import sys, os
|
||||||
|
|
||||||
x += 1
|
x += 1
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:1:0
|
warning:1:
|
||||||
warning:1:0
|
warning:1:
|
||||||
error:3:0
|
error:3:
|
||||||
|
|
|
@ -94,7 +94,7 @@ final class ArcanistFormattedStringXHPASTLinterRule
|
||||||
$argv = array($format->evalStatic()) + array_fill(0, $argc, null);
|
$argv = array($format->evalStatic()) + array_fill(0, $argc, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
xsprintf(null, null, $argv);
|
xsprintf(array(__CLASS__, 'processXsprintfCallback'), null, $argv);
|
||||||
} catch (BadFunctionCallException $ex) {
|
} catch (BadFunctionCallException $ex) {
|
||||||
$this->raiseLintAtNode(
|
$this->raiseLintAtNode(
|
||||||
$call,
|
$call,
|
||||||
|
@ -105,4 +105,23 @@ final class ArcanistFormattedStringXHPASTLinterRule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function processXsprintfCallback(
|
||||||
|
$userdata,
|
||||||
|
&$pattern,
|
||||||
|
&$pos,
|
||||||
|
&$value,
|
||||||
|
&$length) {
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
throw new Exception('Expected dummy value to be null');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn format "%$pattern" with argument null into format "%s" with
|
||||||
|
// argument "%$pattern". This ensures we always provide valid input for
|
||||||
|
// sprintf to avoid getting a ValueError when using custom format
|
||||||
|
// specifiers.
|
||||||
|
$value = '%'.$pattern[$pos];
|
||||||
|
$pattern[$pos] = 's';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@ final class PhutilTypeSpec extends Phobject {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'regex':
|
case 'regex':
|
||||||
|
if (!is_string($value)) {
|
||||||
|
throw new PhutilTypeCheckException($this, $value, $name);
|
||||||
|
}
|
||||||
|
|
||||||
$trap = new PhutilErrorTrap();
|
$trap = new PhutilErrorTrap();
|
||||||
$ok = @preg_match($value, '');
|
$ok = @preg_match($value, '');
|
||||||
$err = $trap->getErrorsAsString();
|
$err = $trap->getErrorsAsString();
|
||||||
|
|
|
@ -27,7 +27,7 @@ final class ArcanistGitCommitMessageHardpointQuery
|
||||||
$futures[$hash] = $api->execFutureLocal(
|
$futures[$hash] = $api->execFutureLocal(
|
||||||
'log -n1 --format=%s %s --',
|
'log -n1 --format=%s %s --',
|
||||||
'%s%n%n%b',
|
'%s%n%n%b',
|
||||||
$hash);
|
gitsprintf('%s', $hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
yield $this->yieldFutures($futures);
|
yield $this->yieldFutures($futures);
|
||||||
|
|
|
@ -88,7 +88,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
*/
|
*/
|
||||||
private function isDescendant($child, $parent) {
|
private function isDescendant($child, $parent) {
|
||||||
list($common_ancestor) = $this->execxLocal(
|
list($common_ancestor) = $this->execxLocal(
|
||||||
'merge-base %s %s',
|
'merge-base -- %s %s',
|
||||||
$child,
|
$child,
|
||||||
$parent);
|
$parent);
|
||||||
$common_ancestor = trim($common_ancestor);
|
$common_ancestor = trim($common_ancestor);
|
||||||
|
@ -214,7 +214,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
list($err, $merge_base) = $this->execManualLocal(
|
list($err, $merge_base) = $this->execManualLocal(
|
||||||
'merge-base %s %s',
|
'merge-base -- %s %s',
|
||||||
$symbolic_commit,
|
$symbolic_commit,
|
||||||
$this->getHeadCommit());
|
$this->getHeadCommit());
|
||||||
if ($err) {
|
if ($err) {
|
||||||
|
@ -381,7 +381,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
list($merge_base) = $this->execxLocal(
|
list($merge_base) = $this->execxLocal(
|
||||||
'merge-base %s HEAD',
|
'merge-base -- %s HEAD',
|
||||||
$default_relative);
|
$default_relative);
|
||||||
|
|
||||||
return trim($merge_base);
|
return trim($merge_base);
|
||||||
|
@ -569,22 +569,24 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
} else if ($relative == self::GIT_MAGIC_ROOT_COMMIT) {
|
} else if ($relative == self::GIT_MAGIC_ROOT_COMMIT) {
|
||||||
// First commit.
|
// First commit.
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'log --format=medium HEAD');
|
'log --format=medium HEAD --');
|
||||||
} else {
|
} else {
|
||||||
// 2..N commits.
|
// 2..N commits.
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'log --first-parent --format=medium %s..%s',
|
'log --first-parent --format=medium %s --',
|
||||||
|
gitsprintf(
|
||||||
|
'%s..%s',
|
||||||
$this->getBaseCommit(),
|
$this->getBaseCommit(),
|
||||||
$this->getHeadCommit());
|
$this->getHeadCommit()));
|
||||||
}
|
}
|
||||||
return $stdout;
|
return $stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGitHistoryLog() {
|
public function getGitHistoryLog() {
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'log --format=medium -n%d %s',
|
'log --format=medium -n%d %s --',
|
||||||
self::SEARCH_LENGTH_FOR_PARENT_REVISIONS,
|
self::SEARCH_LENGTH_FOR_PARENT_REVISIONS,
|
||||||
$this->getBaseCommit());
|
gitsprintf('%s', $this->getBaseCommit()));
|
||||||
return $stdout;
|
return $stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,7 +723,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
array(
|
array(
|
||||||
'diff %C --raw %s --',
|
'diff %C --raw %s --',
|
||||||
$diff_options,
|
$diff_options,
|
||||||
$diff_base,
|
gitsprintf('%s', $diff_base),
|
||||||
));
|
));
|
||||||
|
|
||||||
$untracked_future = $this->buildLocalFuture(
|
$untracked_future = $this->buildLocalFuture(
|
||||||
|
@ -782,7 +784,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
list($stdout, $stderr) = $this->execxLocal(
|
list($stdout, $stderr) = $this->execxLocal(
|
||||||
'diff %C --raw %s HEAD --',
|
'diff %C --raw %s HEAD --',
|
||||||
$this->getDiffBaseOptions(),
|
$this->getDiffBaseOptions(),
|
||||||
$this->getBaseCommit());
|
gitsprintf('%s', $this->getBaseCommit()));
|
||||||
|
|
||||||
return $this->parseGitRawDiff($stdout);
|
return $this->parseGitRawDiff($stdout);
|
||||||
}
|
}
|
||||||
|
@ -935,15 +937,15 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
|
|
||||||
public function getChangedFiles($since_commit) {
|
public function getChangedFiles($since_commit) {
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'diff --raw %s',
|
'diff --raw %s --',
|
||||||
$since_commit);
|
gitsprintf('%s', $since_commit));
|
||||||
return $this->parseGitRawDiff($stdout);
|
return $this->parseGitRawDiff($stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlame($path) {
|
public function getBlame($path) {
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'blame --porcelain -w -M %s -- %s',
|
'blame --porcelain -w -M %s -- %s',
|
||||||
$this->getBaseCommit(),
|
gitsprintf('%s', $this->getBaseCommit()),
|
||||||
$path);
|
$path);
|
||||||
|
|
||||||
// the --porcelain format prints at least one header line per source line,
|
// the --porcelain format prints at least one header line per source line,
|
||||||
|
@ -1030,7 +1032,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
|
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'ls-tree %s -- %s',
|
'ls-tree %s -- %s',
|
||||||
$revision,
|
gitsprintf('%s', $revision),
|
||||||
$path);
|
$path);
|
||||||
|
|
||||||
$info = $this->parseGitTree($stdout);
|
$info = $this->parseGitTree($stdout);
|
||||||
|
@ -1046,7 +1048,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
list($stdout) = $this->execxLocal(
|
list($stdout) = $this->execxLocal(
|
||||||
'cat-file blob %s',
|
'cat-file blob -- %s',
|
||||||
$info[$path]['ref']);
|
$info[$path]['ref']);
|
||||||
return $stdout;
|
return $stdout;
|
||||||
}
|
}
|
||||||
|
@ -1171,7 +1173,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
list($message) = $this->execxLocal(
|
list($message) = $this->execxLocal(
|
||||||
'log -n1 --format=%C %s --',
|
'log -n1 --format=%C %s --',
|
||||||
'%s%n%n%b',
|
'%s%n%n%b',
|
||||||
$commit);
|
gitsprintf('%s', $commit));
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1249,9 +1251,9 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
list($summary) = $this->execxLocal(
|
list($summary) = $this->execxLocal(
|
||||||
'log -n 1 --format=%C %s',
|
'log -n 1 %s %s --',
|
||||||
'%s',
|
'--format=%s',
|
||||||
$commit);
|
gitsprintf('%s', $commit));
|
||||||
|
|
||||||
return trim($summary);
|
return trim($summary);
|
||||||
}
|
}
|
||||||
|
@ -1268,7 +1270,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
$matches = null;
|
$matches = null;
|
||||||
if (preg_match('/^merge-base\((.+)\)$/', $name, $matches)) {
|
if (preg_match('/^merge-base\((.+)\)$/', $name, $matches)) {
|
||||||
list($err, $merge_base) = $this->execManualLocal(
|
list($err, $merge_base) = $this->execManualLocal(
|
||||||
'merge-base %s HEAD',
|
'merge-base -- %s HEAD',
|
||||||
$matches[1]);
|
$matches[1]);
|
||||||
if (!$err) {
|
if (!$err) {
|
||||||
$this->setBaseCommitExplanation(
|
$this->setBaseCommitExplanation(
|
||||||
|
@ -1282,7 +1284,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
}
|
}
|
||||||
} else if (preg_match('/^branch-unique\((.+)\)$/', $name, $matches)) {
|
} else if (preg_match('/^branch-unique\((.+)\)$/', $name, $matches)) {
|
||||||
list($err, $merge_base) = $this->execManualLocal(
|
list($err, $merge_base) = $this->execManualLocal(
|
||||||
'merge-base %s HEAD',
|
'merge-base -- %s HEAD',
|
||||||
$matches[1]);
|
$matches[1]);
|
||||||
if ($err) {
|
if ($err) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1400,7 +1402,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
||||||
if (!$err) {
|
if (!$err) {
|
||||||
$upstream = rtrim($upstream);
|
$upstream = rtrim($upstream);
|
||||||
list($upstream_merge_base) = $this->execxLocal(
|
list($upstream_merge_base) = $this->execxLocal(
|
||||||
'merge-base %s HEAD',
|
'merge-base -- %s HEAD',
|
||||||
$upstream);
|
$upstream);
|
||||||
$upstream_merge_base = rtrim($upstream_merge_base);
|
$upstream_merge_base = rtrim($upstream_merge_base);
|
||||||
$this->setBaseCommitExplanation(
|
$this->setBaseCommitExplanation(
|
||||||
|
|
|
@ -84,7 +84,7 @@ final class ArcanistGitCommitGraphQuery
|
||||||
$format = implode('%x02', $fields).'%x01';
|
$format = implode('%x02', $fields).'%x01';
|
||||||
|
|
||||||
$future = $api->newFuture(
|
$future = $api->newFuture(
|
||||||
'log --format=%s %Ls --stdin',
|
'log --format=%s %Ls --stdin --',
|
||||||
$format,
|
$format,
|
||||||
$flags);
|
$flags);
|
||||||
$future->write($ref_blob);
|
$future->write($ref_blob);
|
||||||
|
|
|
@ -408,7 +408,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
*
|
*
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
final private function failTest($reason) {
|
private function failTest($reason) {
|
||||||
$this->resultTest(ArcanistUnitTestResult::RESULT_FAIL, $reason);
|
$this->resultTest(ArcanistUnitTestResult::RESULT_FAIL, $reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +421,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
*
|
*
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
final private function passTest($reason) {
|
private function passTest($reason) {
|
||||||
$this->resultTest(ArcanistUnitTestResult::RESULT_PASS, $reason);
|
$this->resultTest(ArcanistUnitTestResult::RESULT_PASS, $reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,12 +433,12 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
* @return void
|
* @return void
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
final private function skipTest($reason) {
|
private function skipTest($reason) {
|
||||||
$this->resultTest(ArcanistUnitTestResult::RESULT_SKIP, $reason);
|
$this->resultTest(ArcanistUnitTestResult::RESULT_SKIP, $reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final private function resultTest($test_result, $reason) {
|
private function resultTest($test_result, $reason) {
|
||||||
$coverage = $this->endCoverage();
|
$coverage = $this->endCoverage();
|
||||||
|
|
||||||
$result = new ArcanistUnitTestResult();
|
$result = new ArcanistUnitTestResult();
|
||||||
|
@ -553,7 +553,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
/**
|
/**
|
||||||
* @phutil-external-symbol function xdebug_start_code_coverage
|
* @phutil-external-symbol function xdebug_start_code_coverage
|
||||||
*/
|
*/
|
||||||
final private function beginCoverage() {
|
private function beginCoverage() {
|
||||||
if (!$this->enableCoverage) {
|
if (!$this->enableCoverage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -566,7 +566,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
* @phutil-external-symbol function xdebug_get_code_coverage
|
* @phutil-external-symbol function xdebug_get_code_coverage
|
||||||
* @phutil-external-symbol function xdebug_stop_code_coverage
|
* @phutil-external-symbol function xdebug_stop_code_coverage
|
||||||
*/
|
*/
|
||||||
final private function endCoverage() {
|
private function endCoverage() {
|
||||||
if (!$this->enableCoverage) {
|
if (!$this->enableCoverage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -618,7 +618,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
return $coverage;
|
return $coverage;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function assertCoverageAvailable() {
|
private function assertCoverageAvailable() {
|
||||||
if (!function_exists('xdebug_start_code_coverage')) {
|
if (!function_exists('xdebug_start_code_coverage')) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht("You've enabled code coverage but XDebug is not installed."));
|
pht("You've enabled code coverage but XDebug is not installed."));
|
||||||
|
@ -675,7 +675,7 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
*
|
*
|
||||||
* @return map
|
* @return map
|
||||||
*/
|
*/
|
||||||
final private static function getCallerInfo() {
|
private static function getCallerInfo() {
|
||||||
$callee = array();
|
$callee = array();
|
||||||
$caller = array();
|
$caller = array();
|
||||||
$seen = false;
|
$seen = false;
|
||||||
|
|
|
@ -318,7 +318,7 @@ abstract class AbstractDirectedGraph extends Phobject {
|
||||||
* which cycle.
|
* which cycle.
|
||||||
* @task cycle
|
* @task cycle
|
||||||
*/
|
*/
|
||||||
final private function performCycleDetection($node, array $visited) {
|
private function performCycleDetection($node, array $visited) {
|
||||||
$visited[$node] = true;
|
$visited[$node] = true;
|
||||||
foreach ($this->knownNodes[$node] as $edge) {
|
foreach ($this->knownNodes[$node] as $edge) {
|
||||||
if (isset($visited[$edge])) {
|
if (isset($visited[$edge])) {
|
||||||
|
|
|
@ -498,6 +498,8 @@ final class PhutilUTF8TestCase extends PhutilTestCase {
|
||||||
phutil_utf8_convert('xyz', 'moon language', 'UTF-8');
|
phutil_utf8_convert('xyz', 'moon language', 'UTF-8');
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$caught = $ex;
|
$caught = $ex;
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
$caught = $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertTrue((bool)$caught, pht('Conversion with bogus encoding.'));
|
$this->assertTrue((bool)$caught, pht('Conversion with bogus encoding.'));
|
||||||
|
|
|
@ -728,7 +728,7 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
return $this->workingDirectory;
|
return $this->workingDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function setParentWorkflow($parent_workflow) {
|
private function setParentWorkflow($parent_workflow) {
|
||||||
$this->parentWorkflow = $parent_workflow;
|
$this->parentWorkflow = $parent_workflow;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -1377,7 +1377,7 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function loadBundleFromConduit(
|
private function loadBundleFromConduit(
|
||||||
ConduitClient $conduit,
|
ConduitClient $conduit,
|
||||||
$params) {
|
$params) {
|
||||||
|
|
||||||
|
|
40
src/xsprintf/__tests__/PhutilGitsprintfTestCase.php
Normal file
40
src/xsprintf/__tests__/PhutilGitsprintfTestCase.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhutilGitsprintfTestCase extends PhutilTestCase {
|
||||||
|
|
||||||
|
public function testHgsprintf() {
|
||||||
|
$selectors = array(
|
||||||
|
'HEAD' => 'HEAD',
|
||||||
|
'master' => 'master',
|
||||||
|
'a..b' => 'a..b',
|
||||||
|
'feature^' => 'feature^',
|
||||||
|
'--flag' => false,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($selectors as $input => $expect) {
|
||||||
|
$caught = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$output = gitsprintf('%s', $input);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($caught !== null) {
|
||||||
|
$actual = false;
|
||||||
|
} else {
|
||||||
|
$actual = $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
$expect,
|
||||||
|
$actual,
|
||||||
|
pht(
|
||||||
|
'Result for input "%s".',
|
||||||
|
$input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/xsprintf/gitsprintf.php
Normal file
64
src/xsprintf/gitsprintf.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a Git ref selector. This formatting is important when executing
|
||||||
|
* commands like "git log" which can not unambiguously parse all values as
|
||||||
|
* ref selectors.
|
||||||
|
*
|
||||||
|
* Supports the following conversions:
|
||||||
|
*
|
||||||
|
* %s Ref Selector
|
||||||
|
* Escapes a Git ref selector. In particular, this will reject ref selectors
|
||||||
|
* which Git may interpret as flags.
|
||||||
|
*
|
||||||
|
* %R Raw String
|
||||||
|
* Passes text through unescaped.
|
||||||
|
*/
|
||||||
|
function gitsprintf($pattern /* , ... */) {
|
||||||
|
$args = func_get_args();
|
||||||
|
return xsprintf('xsprintf_git', null, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{function:xsprintf} callback for Git encoding.
|
||||||
|
*/
|
||||||
|
function xsprintf_git($userdata, &$pattern, &$pos, &$value, &$length) {
|
||||||
|
$type = $pattern[$pos];
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 's':
|
||||||
|
|
||||||
|
// See T13589. Some Git commands accept both a ref selector and a list of
|
||||||
|
// paths. For example:
|
||||||
|
|
||||||
|
// $ git log <ref> -- <path> <path> ...
|
||||||
|
|
||||||
|
// These commands disambiguate ref selectors from paths using "--", but
|
||||||
|
// have no mechanism for disambiguating ref selectors from flags.
|
||||||
|
|
||||||
|
// Thus, there appears to be no way (in the general case) to safely
|
||||||
|
// invoke these commands with an arbitrary ref selector string: ref
|
||||||
|
// selector strings like "--flag" may be interpreted as flags, not as
|
||||||
|
// ref selectors.
|
||||||
|
|
||||||
|
// To resolve this, we reject any ref selector which begins with "-".
|
||||||
|
// These selectors are never valid anyway, so there is no loss of overall
|
||||||
|
// correctness. It would be more desirable to pass them to Git in a way
|
||||||
|
// that guarantees Git inteprets the string as a ref selector, but it
|
||||||
|
// appears that no mechanism exists to allow this.
|
||||||
|
|
||||||
|
if (preg_match('(^-)', $value)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Git ref selector "%s" is not a valid selector and can not be '.
|
||||||
|
'passed to the Git CLI safely in the general case.',
|
||||||
|
$value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
$type = 's';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pattern[$pos] = $type;
|
||||||
|
}
|
|
@ -94,8 +94,11 @@ function __arcanist_init_script__() {
|
||||||
)));
|
)));
|
||||||
|
|
||||||
// Disable the insanely dangerous XML entity loader by default.
|
// Disable the insanely dangerous XML entity loader by default.
|
||||||
|
// PHP 8 deprecates this function and disables this by default; remove once
|
||||||
|
// PHP 7 is no longer supported or a future version has removed the function
|
||||||
|
// entirely.
|
||||||
if (function_exists('libxml_disable_entity_loader')) {
|
if (function_exists('libxml_disable_entity_loader')) {
|
||||||
libxml_disable_entity_loader(true);
|
@libxml_disable_entity_loader(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(__FILE__)));
|
$root = dirname(dirname(dirname(__FILE__)));
|
||||||
|
|
Loading…
Reference in a new issue