mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-02 18:08:26 +01:00
Parse "shallow" frames in the Git "upload-pack" wire protocol parser
Summary: Fixes T13309. If you void the warranty on a repository on disk and turn it into a shallow clone, Phabricator currently can't serve it. We don't support hosting shallow working copies, but we should still parse and proxy the protocol rather than breaking in a mysterious way. Test Plan: - Created a shallow working copy with `mv X X.full; git clone --depth Y file://.../X.full X` in the storage directory on disk. - Cloned it with `git clone <uri>`. - Deleted all the refs inside it so the wire only has "shallow" frames; cloned it. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13309 Differential Revision: https://secure.phabricator.com/D20577
This commit is contained in:
parent
7538286499
commit
dda5c13ef5
2 changed files with 80 additions and 39 deletions
|
@ -187,61 +187,76 @@ final class DiffusionGitUploadPackWireProtocol
|
|||
// <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) {
|
||||
$capabilities_pattern = '\0(?P<capabilities>[^\n]+)';
|
||||
} else {
|
||||
$capabilities_pattern = '';
|
||||
}
|
||||
|
||||
$ok = preg_match(
|
||||
'('.
|
||||
'^'.
|
||||
'(?P<hash>[0-9a-f]{40})'.
|
||||
' '.
|
||||
'(?P<name>[^\0\n]+)'.
|
||||
'\0'.
|
||||
'(?P<capabilities>[^\n]+)'.
|
||||
'(?:'.
|
||||
'(?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) {
|
||||
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'];
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -252,10 +267,16 @@ final class DiffusionGitUploadPackWireProtocol
|
|||
->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,
|
||||
|
@ -275,10 +296,19 @@ final class DiffusionGitUploadPackWireProtocol
|
|||
// 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(),
|
||||
|
|
|
@ -5,6 +5,7 @@ final class DiffusionGitWireProtocolRef
|
|||
|
||||
private $name;
|
||||
private $hash;
|
||||
private $isShallow;
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
|
@ -24,9 +25,19 @@ final class DiffusionGitWireProtocolRef
|
|||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue