Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php b/src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php
index 451ad74c9e..724c1c83b2 100644
--- a/src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php
+++ b/src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php
@@ -1,325 +1,355 @@
<?php
final class DiffusionGitUploadPackWireProtocol
extends DiffusionGitWireProtocol {
private $readMode = 'length';
private $readBuffer;
private $readFrameLength;
private $readFrames = array();
private $readFrameMode = 'refs';
private $refFrames = array();
private $readMessages = array();
public function willReadBytes($bytes) {
if ($this->readBuffer === null) {
$this->readBuffer = new PhutilRope();
}
$buffer = $this->readBuffer;
$buffer->append($bytes);
while (true) {
$len = $buffer->getByteLength();
switch ($this->readMode) {
case 'length':
// We're expecting 4 bytes containing the length of the protocol
// frame as hexadecimal in ASCII text, like "01ab". Wait until we
// see at least 4 bytes on the wire.
if ($len < 4) {
if ($len > 0) {
$bytes = $this->peekBytes($len);
if (!preg_match('/^[0-9a-f]+\z/', $bytes)) {
throw new Exception(
pht(
'Bad frame length character in Git protocol ("%s"), '.
'expected a 4-digit hexadecimal value encoded as ASCII '.
'text.',
$bytes));
}
}
// We can't make any more progress until we get enough bytes, so
// we're done with state processing.
break 2;
}
$frame_length = $this->readBytes(4);
$frame_length = hexdec($frame_length);
// Note that the frame length includes the 4 header bytes, so we
// usually expect a length of 5 or larger. Frames with length 0
// are boundaries.
if ($frame_length === 0) {
$this->readFrames[] = $this->newProtocolFrame('null', '');
} else if ($frame_length >= 1 && $frame_length <= 3) {
throw new Exception(
pht(
'Encountered Git protocol frame with unexpected frame '.
'length (%s)!',
$frame_length));
} else {
$this->readFrameLength = $frame_length - 4;
$this->readMode = 'frame';
}
break;
case 'frame':
// We're expecting a protocol frame of a specified length. Note that
// it is possible for a frame to have length 0.
// We don't have enough bytes yet, so wait for more.
if ($len < $this->readFrameLength) {
break 2;
}
if ($this->readFrameLength > 0) {
$bytes = $this->readBytes($this->readFrameLength);
} else {
$bytes = '';
}
// Emit a protocol frame.
$this->readFrames[] = $this->newProtocolFrame('data', $bytes);
$this->readMode = 'length';
break;
}
}
while (true) {
switch ($this->readFrameMode) {
case 'refs':
if (!$this->readFrames) {
break 2;
}
foreach ($this->readFrames as $key => $frame) {
unset($this->readFrames[$key]);
if ($frame['type'] === 'null') {
$ref_frames = $this->refFrames;
$this->refFrames = array();
$ref_frames[] = $frame;
$this->readMessages[] = $this->newProtocolRefMessage($ref_frames);
$this->readFrameMode = 'passthru';
break;
} else {
$this->refFrames[] = $frame;
}
}
break;
case 'passthru':
if (!$this->readFrames) {
break 2;
}
$this->readMessages[] = $this->newProtocolDataMessage(
$this->readFrames);
$this->readFrames = array();
break;
}
}
$wire = array();
foreach ($this->readMessages as $key => $message) {
$wire[] = $message;
unset($this->readMessages[$key]);
}
$wire = implode('', $wire);
return $wire;
}
public function willWriteBytes($bytes) {
return $bytes;
}
private function readBytes($count) {
$buffer = $this->readBuffer;
$bytes = $buffer->getPrefixBytes($count);
$buffer->removeBytesFromHead($count);
return $bytes;
}
private function peekBytes($count) {
$buffer = $this->readBuffer;
return $buffer->getPrefixBytes($count);
}
private function newProtocolFrame($type, $bytes) {
return array(
'type' => $type,
'length' => strlen($bytes),
'bytes' => $bytes,
);
}
private function newProtocolRefMessage(array $frames) {
$head_key = head_key($frames);
$last_key = last_key($frames);
$capabilities = null;
$last_frame = null;
$refs = array();
foreach ($frames as $key => $frame) {
$is_last = ($key === $last_key);
if ($is_last) {
// This is a "0000" frame at the end of the list of refs, so we pass
// it through unmodified after we figure out what the rest of the
// frames should look like, below.
$last_frame = $frame;
continue;
}
$is_first = ($key === $head_key);
// Otherwise, we expect a list of:
//
// <hash> <ref-name>\0<capabilities>
// <hash> <ref-name>
// ...
+ //
+ // See T13309. The end of this list (which may be empty if a repository
+ // does not have any refs) has a list of zero or more of these:
+ //
+ // shallow <hash>
+ //
+ // These entries are present if the repository is a shallow clone
+ // which was made with the "--depth" flag.
+ //
+ // Note that "shallow" frames do not advertise capabilities, and if
+ // a repository has only "shallow" frames, capabilities are never
+ // advertised.
$bytes = $frame['bytes'];
$matches = array();
if ($is_first) {
- $ok = preg_match(
- '('.
- '^'.
- '(?P<hash>[0-9a-f]{40})'.
- ' '.
- '(?P<name>[^\0\n]+)'.
- '\0'.
- '(?P<capabilities>[^\n]+)'.
- '\n'.
- '\z'.
- ')',
- $bytes,
- $matches);
- if (!$ok) {
+ $capabilities_pattern = '\0(?P<capabilities>[^\n]+)';
+ } else {
+ $capabilities_pattern = '';
+ }
+
+ $ok = preg_match(
+ '('.
+ '^'.
+ '(?:'.
+ '(?P<hash>[0-9a-f]{40}) (?P<name>[^\0\n]+)'.$capabilities_pattern.
+ '|'.
+ 'shallow (?P<shallow>[0-9a-f]{40})'.
+ ')'.
+ '\n'.
+ '\z'.
+ ')',
+ $bytes,
+ $matches);
+
+ if (!$ok) {
+ if ($is_first) {
throw new Exception(
pht(
'Unexpected "git upload-pack" initial protocol frame: expected '.
- '"<hash> <name>\0<capabilities>\n", got "%s".',
+ '"<hash> <name>\0<capabilities>\n", or '.
+ '"shallow <hash>\n", got "%s".',
$bytes));
- }
- } else {
- $ok = preg_match(
- '('.
- '^'.
- '(?P<hash>[0-9a-f]{40})'.
- ' '.
- '(?P<name>[^\0\n]+)'.
- '\n'.
- '\z'.
- ')',
- $bytes,
- $matches);
- if (!$ok) {
+ } else {
throw new Exception(
pht(
'Unexpected "git upload-pack" protocol frame: expected '.
- '"<hash> <name>\n", got "%s".',
+ '"<hash> <name>\n", or "shallow <hash>\n", got "%s".',
$bytes));
}
}
- $hash = $matches['hash'];
- $name = $matches['name'];
+ if (isset($matches['shallow'])) {
+ $name = null;
+ $hash = $matches['shallow'];
+ $is_shallow = true;
+ } else {
+ $name = $matches['name'];
+ $hash = $matches['hash'];
+ $is_shallow = false;
+ }
- if ($is_first) {
+ if (isset($matches['capabilities'])) {
$capabilities = $matches['capabilities'];
}
$refs[] = array(
'hash' => $hash,
'name' => $name,
+ 'shallow' => $is_shallow,
);
}
$capabilities = DiffusionGitWireProtocolCapabilities::newFromWireFormat(
$capabilities);
$ref_list = id(new DiffusionGitWireProtocolRefList())
->setCapabilities($capabilities);
foreach ($refs as $ref) {
- $ref_list->addRef(
- id(new DiffusionGitWireProtocolRef())
- ->setName($ref['name'])
- ->setHash($ref['hash']));
+ $wire_ref = id(new DiffusionGitWireProtocolRef())
+ ->setHash($ref['hash']);
+
+ if ($ref['shallow']) {
+ $wire_ref->setIsShallow(true);
+ } else {
+ $wire_ref->setName($ref['name']);
+ }
+
+ $ref_list->addRef($wire_ref);
}
// TODO: Here, we have a structured list of refs. In a future change,
// we are free to mutate the structure before flattening it back into
// wire format.
$refs = $ref_list->getRefs();
// Before we write the ref list, sort it for consistency with native
// Git output. We may have added, removed, or renamed refs and ended up
// with an out-of-order list.
$refs = msortv($refs, 'newSortVector');
// The first ref we send back includes the capabilities data. Note that if
// we send back no refs, we also don't send back capabilities! This is
// a little surprising, but is consistent with the native behavior of the
// protocol.
+ // Likewise, we don't send back any capabilities if we're sending only
+ // "shallow" frames.
+
$output = array();
$is_first = true;
foreach ($refs as $ref) {
- if ($is_first) {
+ $is_shallow = $ref->getIsShallow();
+
+ if ($is_shallow) {
+ $result = sprintf(
+ "shallow %s\n",
+ $ref->getHash());
+ } else if ($is_first) {
$result = sprintf(
"%s %s\0%s\n",
$ref->getHash(),
$ref->getName(),
$ref_list->getCapabilities()->toWireFormat());
} else {
$result = sprintf(
"%s %s\n",
$ref->getHash(),
$ref->getName());
}
$output[] = $this->newProtocolFrame('data', $result);
$is_first = false;
}
$output[] = $last_frame;
return $this->newProtocolDataMessage($output);
}
private function newProtocolDataMessage(array $frames) {
$message = array();
foreach ($frames as $frame) {
switch ($frame['type']) {
case 'null':
$message[] = '0000';
break;
case 'data':
$message[] = sprintf(
'%04x%s',
$frame['length'] + 4,
$frame['bytes']);
break;
}
}
$message = implode('', $message);
return $message;
}
}
diff --git a/src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php b/src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php
index bf5238c219..bd1672317a 100644
--- a/src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php
+++ b/src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php
@@ -1,32 +1,43 @@
<?php
final class DiffusionGitWireProtocolRef
extends Phobject {
private $name;
private $hash;
+ private $isShallow;
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setHash($hash) {
$this->hash = $hash;
return $this;
}
public function getHash() {
return $this->hash;
}
+ public function setIsShallow($is_shallow) {
+ $this->isShallow = $is_shallow;
+ return $this;
+ }
+
+ public function getIsShallow() {
+ return $this->isShallow;
+ }
+
public function newSortVector() {
return id(new PhutilSortVector())
- ->addString($this->getName());
+ ->addInt((int)$this->getIsShallow())
+ ->addString((string)$this->getName());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 8:14 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140933
Default Alt Text
(12 KB)

Event Timeline