1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-29 10:12:41 +01:00

(experimental) Merge branch "master" into "experimental".

This commit is contained in:
epriestley 2018-04-30 11:40:15 -07:00
commit 290821d3f2
9 changed files with 264 additions and 122 deletions

View file

@ -781,37 +781,46 @@ final class ArcanistBundle extends Phobject {
private function emitBinaryDiffBody($data) { private function emitBinaryDiffBody($data) {
$eol = $this->getEOL('git'); $eol = $this->getEOL('git');
return self::newBase85Data($data, $eol);
}
if (!function_exists('gzcompress')) { public static function newBase85Data($data, $eol, $mode = null) {
throw new Exception( // The "32bit" and "64bit" modes are used by unit tests to verify that all
pht( // of the encoding pathways here work identically. In these modes, we skip
'This patch has binary data. The PHP zlib extension is required to '. // compression because `gzcompress()` may not be stable and we just want
'apply patches with binary data to git. Install the PHP zlib '. // to test that the output matches some expected result.
'extension to continue.'));
if ($mode === null) {
if (!function_exists('gzcompress')) {
throw new Exception(
pht(
'This patch has binary data. The PHP zlib extension is required '.
'to apply patches with binary data to git. Install the PHP zlib '.
'extension to continue.'));
}
$input = gzcompress($data);
$is_64bit = (PHP_INT_SIZE >= 8);
} else {
switch ($mode) {
case '32bit':
$input = $data;
$is_64bit = false;
break;
case '64bit':
$input = $data;
$is_64bit = true;
break;
default:
throw new Exception(
pht(
'Unsupported base85 encoding mode "%s".',
$mode));
}
} }
// See emit_binary_diff_body() in diff.c for git's implementation. // See emit_binary_diff_body() in diff.c for git's implementation.
$buf = '';
$deflated = gzcompress($data);
$lines = str_split($deflated, 52);
foreach ($lines as $line) {
$len = strlen($line);
// The first character encodes the line length.
if ($len <= 26) {
$buf .= chr($len + ord('A') - 1);
} else {
$buf .= chr($len - 26 + ord('a') - 1);
}
$buf .= self::encodeBase85($line);
$buf .= $eol;
}
return $buf;
}
public static function encodeBase85($data) {
// This is implemented awkwardly in order to closely mirror git's // This is implemented awkwardly in order to closely mirror git's
// implementation in base85.c // implementation in base85.c
@ -839,6 +848,9 @@ final class ArcanistBundle extends Phobject {
// (Since PHP overflows integer operations into floats, we don't need much // (Since PHP overflows integer operations into floats, we don't need much
// additional casting.) // additional casting.)
// On 64 bit systems, we skip all this fanfare and just use integers. This
// is significantly faster.
static $map = array( static $map = array(
'0', '0',
'1', '1',
@ -927,27 +939,65 @@ final class ArcanistBundle extends Phobject {
'~', '~',
); );
$len_map = array();
for ($ii = 0; $ii <= 52; $ii++) {
if ($ii <= 26) {
$len_map[$ii] = chr($ii + ord('A') - 1);
} else {
$len_map[$ii] = chr($ii - 26 + ord('a') - 1);
}
}
$buf = ''; $buf = '';
$pos = 0; $lines = str_split($input, 52);
$bytes = strlen($data); $final = (count($lines) - 1);
while ($bytes) {
$accum = 0; foreach ($lines as $idx => $line) {
for ($count = 24; $count >= 0; $count -= 8) { if ($idx === $final) {
$val = ord($data[$pos++]); $len = strlen($line);
$val = $val * (1 << $count); } else {
$accum = $accum + $val; $len = 52;
if (--$bytes == 0) { }
break;
// The first character encodes the line length.
$buf .= $len_map[$len];
$pos = 0;
while ($len) {
$accum = 0;
for ($count = 24; $count >= 0; $count -= 8) {
$val = ord($line[$pos++]);
$val = $val * (1 << $count);
$accum = $accum + $val;
if (--$len == 0) {
break;
}
} }
$slice = '';
// If we're in 64bit mode, we can just use integers. Otherwise, we
// need to use floating point math to avoid overflows.
if ($is_64bit) {
for ($count = 4; $count >= 0; $count--) {
$val = $accum % 85;
$accum = $accum / 85;
$slice .= $map[$val];
}
} else {
for ($count = 4; $count >= 0; $count--) {
$val = (int)fmod($accum, 85.0);
$accum = floor($accum / 85.0);
$slice .= $map[$val];
}
}
$buf .= strrev($slice);
} }
$slice = '';
for ($count = 4; $count >= 0; $count--) { $buf .= $eol;
$val = (int)fmod($accum, 85.0);
$accum = floor($accum / 85.0);
$slice .= $map[$val];
}
$buf .= strrev($slice);
} }
return $buf; return $buf;

View file

@ -663,11 +663,7 @@ EODIFF;
} }
$expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect1.txt'); $expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect1.txt');
$expect = trim($expect); $this->assertBase85($expect, $data, pht('Byte Sequences'));
$this->assertEqual(
$expect,
ArcanistBundle::encodeBase85($data));
// This is just a large block of random binary data, it has no special // This is just a large block of random binary data, it has no special
// significance. // significance.
@ -948,11 +944,27 @@ EODIFF;
"\xe8\x1d\xa4\x18\xf3\x73\x82\xb4\x50\x59\xc2\x34\x36\x05\xeb"; "\xe8\x1d\xa4\x18\xf3\x73\x82\xb4\x50\x59\xc2\x34\x36\x05\xeb";
$expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect2.txt'); $expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect2.txt');
$expect = trim($expect);
$this->assertEqual( $this->assertBase85($expect, $data, pht('Random Data'));
$expect, }
ArcanistBundle::encodeBase85($data));
private function assertBase85($expect, $data, $label) {
$modes = array(
'32bit',
);
// If this is a 64-bit machine, we can also test 64-bit mode.
$has_64bit = (PHP_INT_SIZE >= 8);
if ($has_64bit) {
$modes[] = '64bit';
}
foreach ($modes as $mode) {
$this->assertEqual(
$expect,
ArcanistBundle::newBase85Data($data, "\n", $mode),
pht('base85/%s: %s', $mode, $label));
}
} }
} }

View file

@ -1 +1,10 @@
009C61O)~M2nh-c3=Iws5D^j+6crX17#SKH9337XAR!_nBqb&%C@Cr{EG;fCFflSSG&MFiI5|2yJUu=?KtV!7L`6nNNJ&adOifNtP*GA-R8>}2SXo+ITwPvYU}0ioWMyV&XlZI|Y;A6DaB*^Tbai%jczJqze0_d@fPsR8goTEOh>41ejE#<ukdcy;l$Dm3n3<ZJoSmMZprN9pq@|{(sHv)}tgWuEu(7hUw6(UkxVgH!yuH4^z`?@9#Kp$P$jQpf%+1cv(9zP<)YaD4*xB0K+}+;a;Njxq<mKk)=;`X~?CtLF@bU8V^!4`l`1$(#{Qds_|Ni~_`}+C#_xAPl^YZcV@9ypF>+0#~=jP?)<Kp4q-`?Ha+uGUK*Vfh4)6&t<&(6)v%gV{f$Hv9P!@|M9zrMY^ySll!x3;ykv$C<Uudc1EtE#D}r>3Q(qoSdppPrqZo0^%JmzI^3lai5;kB*Iui;9VehlYiOgMxv8e|~*@dwO|zcXoAjb8>NTZ*FaDYiem|XJ%z&V`5=oUtV2YTUuFIS5{S2Q&Le-PfkrtOG-&dM@B_NLqb77KR!J?J32WyH#RjiGcqwSFD@-CD=H}{CnhB%BO)OnA08bX8yXoH7Zw#16A}>+4-O3s3knGc2L=TM0|Eg6 z009C61O)~M2nh-c3=Iws5D^j+6crX17#SKH9337XAR!_nBqb&%C@Cr{EG;fCFflSS
zG&MFiI5|2yJUu=?KtV!7L`6nNNJ&adOifNtP*GA-R8>}2SXo+ITwPvYU}0ioWMyV&
zXlZI|Y;A6DaB*^Tbai%jczJqze0_d@fPsR8goTEOh>41ejE#<ukdcy;l$Dm3n3<ZJ
zoSmMZprN9pq@|{(sHv)}tgWuEu(7hUw6(UkxVgH!yuH4^z`?@9#Kp$P$jQpf%+1cv
z(9zP<)YaD4*xB0K+}+;a;Njxq<mKk)=;`X~?CtLF@bU8V^!4`l`1$(#{Qds_|Ni~_
z`}+C#_xAPl^YZcV@9ypF>+0#~=jP?)<Kp4q-`?Ha+uGUK*Vfh4)6&t<&(6)v%gV{f
z$Hv9P!@|M9zrMY^ySll!x3;ykv$C<Uudc1EtE#D}r>3Q(qoSdppPrqZo0^%JmzI^3
zlai5;kB*Iui;9VehlYiOgMxv8e|~*@dwO|zcXoAjb8>NTZ*FaDYiem|XJ%z&V`5=o
zUtV2YTUuFIS5{S2Q&Le-PfkrtOG-&dM@B_NLqb77KR!J?J32WyH#RjiGcqwSFD@-C
rD=H}{CnhB%BO)OnA08bX8yXoH7Zw#16A}>+4-O3s3knGc2L=TM0|Eg6

File diff suppressed because one or more lines are too long

View file

@ -660,14 +660,18 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$parts = explode("\0", $stdout); $parts = explode("\0", $stdout);
while (count($parts) > 1) { while (count($parts) > 1) {
$entry = array_shift($parts); $entry = array_shift($parts);
$entry_parts = explode(' ', $entry); $entry_parts = explode(' ', $entry, 2);
if ($entry_parts[0] == '1') { if ($entry_parts[0] == '1') {
$entry_parts = explode(' ', $entry, 9);
$path = $entry_parts[8]; $path = $entry_parts[8];
} else if ($entry_parts[0] == '2') { } else if ($entry_parts[0] == '2') {
$entry_parts = explode(' ', $entry, 10);
$path = $entry_parts[9]; $path = $entry_parts[9];
} else if ($entry_parts[0] == 'u') { } else if ($entry_parts[0] == 'u') {
$entry_parts = explode(' ', $entry, 11);
$path = $entry_parts[10]; $path = $entry_parts[10];
} else if ($entry_parts[0] == '?') { } else if ($entry_parts[0] == '?') {
$entry_parts = explode(' ', $entry, 2);
$result[$entry_parts[1]] = self::FLAG_UNTRACKED; $result[$entry_parts[1]] = self::FLAG_UNTRACKED;
continue; continue;
} }

View file

@ -7,6 +7,7 @@ final class ArcanistRepositoryAPIStateTestCase extends PhutilTestCase {
$this->parseState('git_basic.git.tgz'); $this->parseState('git_basic.git.tgz');
$this->parseState('git_submodules_dirty.git.tgz'); $this->parseState('git_submodules_dirty.git.tgz');
$this->parseState('git_submodules_staged.git.tgz'); $this->parseState('git_submodules_staged.git.tgz');
$this->parseState('git_spaces.git.tgz');
} else { } else {
$this->assertSkipped(pht('Git is not installed')); $this->assertSkipped(pht('Git is not installed'));
} }
@ -156,6 +157,17 @@ final class ArcanistRepositoryAPIStateTestCase extends PhutilTestCase {
); );
$this->assertEqual($expect_uncommitted, $api->getUncommittedStatus()); $this->assertEqual($expect_uncommitted, $api->getUncommittedStatus());
break; break;
case 'git_spaces.git.tgz':
$expect_working = array(
'SPACES ADDED' => $f_add,
'SPACES DELETED' => $f_del,
'SPACES MODIFIED' => $f_mod,
'SPACES UNCOMMITTED' => $f_add | $f_unc,
'SPACES UNSTAGED' => $f_add | $f_mod | $f_uns | $f_unc,
'SPACES UNTRACKED' => $f_unt,
);
$this->assertEqual($expect_working, $api->getWorkingCopyStatus());
break;
case 'hg_basic.hg.tgz': case 'hg_basic.hg.tgz':
$expect_uncommitted = array( $expect_uncommitted = array(
'UNCOMMITTED' => $f_mod | $f_unc, 'UNCOMMITTED' => $f_mod | $f_unc,

Binary file not shown.

View file

@ -994,22 +994,6 @@ EOTEXT
throw new Exception(pht('Repository API is not supported.')); throw new Exception(pht('Repository API is not supported.'));
} }
if (count($changes) > 250) {
$message = pht(
'This diff has a very large number of changes (%s). Differential '.
'works best for changes which will receive detailed human review, '.
'and not as well for large automated changes or bulk checkins. '.
'See %s for information about reviewing big checkins. Continue anyway?',
phutil_count($changes),
'https://secure.phabricator.com/book/phabricator/article/'.
'differential_large_changes/');
if (!phutil_console_confirm($message)) {
throw new ArcanistUsageException(
pht('Aborted generation of gigantic diff.'));
}
}
$limit = 1024 * 1024 * 4; $limit = 1024 * 1024 * 4;
foreach ($changes as $change) { foreach ($changes as $change) {
$size = 0; $size = 0;

View file

@ -115,66 +115,59 @@ EOTEXT
} }
protected function didParseArguments() { protected function didParseArguments() {
$source = null; $arguments = array(
$requested = 0; 'revision' => self::SOURCE_REVISION,
if ($this->getArgument('revision')) { 'diff' => self::SOURCE_DIFF,
$source = self::SOURCE_REVISION; 'arcbundle' => self::SOURCE_BUNDLE,
$requested++; 'patch' => self::SOURCE_PATCH,
} 'name' => self::SOURCE_REVISION,
if ($this->getArgument('diff')) { );
$source = self::SOURCE_DIFF;
$requested++;
}
if ($this->getArgument('arcbundle')) {
$source = self::SOURCE_BUNDLE;
$requested++;
}
if ($this->getArgument('patch')) {
$source = self::SOURCE_PATCH;
$requested++;
}
$use_revision_id = null; $sources = array();
if ($this->getArgument('name')) { foreach ($arguments as $key => $source_type) {
$namev = $this->getArgument('name'); $value = $this->getArgument($key);
if (count($namev) > 1) { if (!$value) {
throw new ArcanistUsageException( continue;
pht('Specify at most one revision name.'));
} }
$source = self::SOURCE_REVISION;
$requested++;
$use_revision_id = $this->normalizeRevisionID(head($namev)); switch ($key) {
case 'revision':
$value = $this->normalizeRevisionID($value);
break;
case 'name':
if (count($value) > 1) {
throw new ArcanistUsageException(
pht('Specify at most one revision name.'));
}
$value = $this->normalizeRevisionID(head($value));
break;
}
$sources[] = array(
$source_type,
$value,
);
} }
if ($requested === 0) { if (!$sources) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
pht( pht(
"Specify one of '%s', '%s' (to select the current changes attached ". 'You must specify changes to apply to the working copy with '.
"to a Differential revision), '%s' (to select a specific, ". '"D12345", "--revision", "--diff", "--arcbundle", or "--patch".'));
"out-of-date diff or a diff which is not attached to a revision), ".
"'%s' or '%s' to choose a patch source.",
'D12345',
'--revision <revision_id>',
'--diff <diff_id>',
'--arcbundle <file>',
'--patch <file>'));
} else if ($requested > 1) {
throw new ArcanistUsageException(
pht(
"Options '%s', '%s', '%s', '%s' and '%s' are not compatible. ".
"Choose exactly one patch source.",
'D12345',
'--revision',
'--diff',
'--arcbundle',
'--patch'));
} }
$this->source = $source; if (count($sources) > 1) {
$this->sourceParam = nonempty( throw new ArcanistUsageException(
$use_revision_id, pht(
$this->getArgument($source)); 'Options "D12345", "--revision", "--diff", "--arcbundle" and '.
'"--patch" are mutually exclusive. Choose exactly one patch '.
'source.'));
}
$source = head($sources);
$this->source = $source[0];
$this->sourceParam = $source[1];
} }
public function requiresConduit() { public function requiresConduit() {