mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-09 05:18:29 +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>\0<capabilities>
|
||||||
// <hash> <ref-name>
|
// <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'];
|
$bytes = $frame['bytes'];
|
||||||
$matches = array();
|
$matches = array();
|
||||||
if ($is_first) {
|
if ($is_first) {
|
||||||
$ok = preg_match(
|
$capabilities_pattern = '\0(?P<capabilities>[^\n]+)';
|
||||||
'('.
|
} else {
|
||||||
'^'.
|
$capabilities_pattern = '';
|
||||||
'(?P<hash>[0-9a-f]{40})'.
|
}
|
||||||
' '.
|
|
||||||
'(?P<name>[^\0\n]+)'.
|
$ok = preg_match(
|
||||||
'\0'.
|
'('.
|
||||||
'(?P<capabilities>[^\n]+)'.
|
'^'.
|
||||||
'\n'.
|
'(?:'.
|
||||||
'\z'.
|
'(?P<hash>[0-9a-f]{40}) (?P<name>[^\0\n]+)'.$capabilities_pattern.
|
||||||
')',
|
'|'.
|
||||||
$bytes,
|
'shallow (?P<shallow>[0-9a-f]{40})'.
|
||||||
$matches);
|
')'.
|
||||||
if (!$ok) {
|
'\n'.
|
||||||
|
'\z'.
|
||||||
|
')',
|
||||||
|
$bytes,
|
||||||
|
$matches);
|
||||||
|
|
||||||
|
if (!$ok) {
|
||||||
|
if ($is_first) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unexpected "git upload-pack" initial protocol frame: expected '.
|
'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));
|
$bytes));
|
||||||
}
|
} else {
|
||||||
} else {
|
|
||||||
$ok = preg_match(
|
|
||||||
'('.
|
|
||||||
'^'.
|
|
||||||
'(?P<hash>[0-9a-f]{40})'.
|
|
||||||
' '.
|
|
||||||
'(?P<name>[^\0\n]+)'.
|
|
||||||
'\n'.
|
|
||||||
'\z'.
|
|
||||||
')',
|
|
||||||
$bytes,
|
|
||||||
$matches);
|
|
||||||
if (!$ok) {
|
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unexpected "git upload-pack" protocol frame: expected '.
|
'Unexpected "git upload-pack" protocol frame: expected '.
|
||||||
'"<hash> <name>\n", got "%s".',
|
'"<hash> <name>\n", or "shallow <hash>\n", got "%s".',
|
||||||
$bytes));
|
$bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash = $matches['hash'];
|
if (isset($matches['shallow'])) {
|
||||||
$name = $matches['name'];
|
$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'];
|
$capabilities = $matches['capabilities'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$refs[] = array(
|
$refs[] = array(
|
||||||
'hash' => $hash,
|
'hash' => $hash,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'shallow' => $is_shallow,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,10 +267,16 @@ final class DiffusionGitUploadPackWireProtocol
|
||||||
->setCapabilities($capabilities);
|
->setCapabilities($capabilities);
|
||||||
|
|
||||||
foreach ($refs as $ref) {
|
foreach ($refs as $ref) {
|
||||||
$ref_list->addRef(
|
$wire_ref = id(new DiffusionGitWireProtocolRef())
|
||||||
id(new DiffusionGitWireProtocolRef())
|
->setHash($ref['hash']);
|
||||||
->setName($ref['name'])
|
|
||||||
->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,
|
// 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
|
// a little surprising, but is consistent with the native behavior of the
|
||||||
// protocol.
|
// protocol.
|
||||||
|
|
||||||
|
// Likewise, we don't send back any capabilities if we're sending only
|
||||||
|
// "shallow" frames.
|
||||||
|
|
||||||
$output = array();
|
$output = array();
|
||||||
$is_first = true;
|
$is_first = true;
|
||||||
foreach ($refs as $ref) {
|
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(
|
$result = sprintf(
|
||||||
"%s %s\0%s\n",
|
"%s %s\0%s\n",
|
||||||
$ref->getHash(),
|
$ref->getHash(),
|
||||||
|
|
|
@ -5,6 +5,7 @@ final class DiffusionGitWireProtocolRef
|
||||||
|
|
||||||
private $name;
|
private $name;
|
||||||
private $hash;
|
private $hash;
|
||||||
|
private $isShallow;
|
||||||
|
|
||||||
public function setName($name) {
|
public function setName($name) {
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
@ -24,9 +25,19 @@ final class DiffusionGitWireProtocolRef
|
||||||
return $this->hash;
|
return $this->hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setIsShallow($is_shallow) {
|
||||||
|
$this->isShallow = $is_shallow;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsShallow() {
|
||||||
|
return $this->isShallow;
|
||||||
|
}
|
||||||
|
|
||||||
public function newSortVector() {
|
public function newSortVector() {
|
||||||
return id(new PhutilSortVector())
|
return id(new PhutilSortVector())
|
||||||
->addString($this->getName());
|
->addInt((int)$this->getIsShallow())
|
||||||
|
->addString((string)$this->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue