@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+80 -39
+68 -38
src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php
··· 187 187 // <hash> <ref-name>\0<capabilities> 188 188 // <hash> <ref-name> 189 189 // ... 190 + // 191 + // See T13309. The end of this list (which may be empty if a repository 192 + // does not have any refs) has a list of zero or more of these: 193 + // 194 + // shallow <hash> 195 + // 196 + // These entries are present if the repository is a shallow clone 197 + // which was made with the "--depth" flag. 198 + // 199 + // Note that "shallow" frames do not advertise capabilities, and if 200 + // a repository has only "shallow" frames, capabilities are never 201 + // advertised. 190 202 191 203 $bytes = $frame['bytes']; 192 204 $matches = array(); 193 205 if ($is_first) { 194 - $ok = preg_match( 195 - '('. 196 - '^'. 197 - '(?P<hash>[0-9a-f]{40})'. 198 - ' '. 199 - '(?P<name>[^\0\n]+)'. 200 - '\0'. 201 - '(?P<capabilities>[^\n]+)'. 202 - '\n'. 203 - '\z'. 204 - ')', 205 - $bytes, 206 - $matches); 207 - if (!$ok) { 206 + $capabilities_pattern = '\0(?P<capabilities>[^\n]+)'; 207 + } else { 208 + $capabilities_pattern = ''; 209 + } 210 + 211 + $ok = preg_match( 212 + '('. 213 + '^'. 214 + '(?:'. 215 + '(?P<hash>[0-9a-f]{40}) (?P<name>[^\0\n]+)'.$capabilities_pattern. 216 + '|'. 217 + 'shallow (?P<shallow>[0-9a-f]{40})'. 218 + ')'. 219 + '\n'. 220 + '\z'. 221 + ')', 222 + $bytes, 223 + $matches); 224 + 225 + if (!$ok) { 226 + if ($is_first) { 208 227 throw new Exception( 209 228 pht( 210 229 'Unexpected "git upload-pack" initial protocol frame: expected '. 211 - '"<hash> <name>\0<capabilities>\n", got "%s".', 230 + '"<hash> <name>\0<capabilities>\n", or '. 231 + '"shallow <hash>\n", got "%s".', 212 232 $bytes)); 213 - } 214 - } else { 215 - $ok = preg_match( 216 - '('. 217 - '^'. 218 - '(?P<hash>[0-9a-f]{40})'. 219 - ' '. 220 - '(?P<name>[^\0\n]+)'. 221 - '\n'. 222 - '\z'. 223 - ')', 224 - $bytes, 225 - $matches); 226 - if (!$ok) { 233 + } else { 227 234 throw new Exception( 228 235 pht( 229 236 'Unexpected "git upload-pack" protocol frame: expected '. 230 - '"<hash> <name>\n", got "%s".', 237 + '"<hash> <name>\n", or "shallow <hash>\n", got "%s".', 231 238 $bytes)); 232 239 } 233 240 } 234 241 235 - $hash = $matches['hash']; 236 - $name = $matches['name']; 242 + if (isset($matches['shallow'])) { 243 + $name = null; 244 + $hash = $matches['shallow']; 245 + $is_shallow = true; 246 + } else { 247 + $name = $matches['name']; 248 + $hash = $matches['hash']; 249 + $is_shallow = false; 250 + } 237 251 238 - if ($is_first) { 252 + if (isset($matches['capabilities'])) { 239 253 $capabilities = $matches['capabilities']; 240 254 } 241 255 242 256 $refs[] = array( 243 257 'hash' => $hash, 244 258 'name' => $name, 259 + 'shallow' => $is_shallow, 245 260 ); 246 261 } 247 262 ··· 252 267 ->setCapabilities($capabilities); 253 268 254 269 foreach ($refs as $ref) { 255 - $ref_list->addRef( 256 - id(new DiffusionGitWireProtocolRef()) 257 - ->setName($ref['name']) 258 - ->setHash($ref['hash'])); 270 + $wire_ref = id(new DiffusionGitWireProtocolRef()) 271 + ->setHash($ref['hash']); 272 + 273 + if ($ref['shallow']) { 274 + $wire_ref->setIsShallow(true); 275 + } else { 276 + $wire_ref->setName($ref['name']); 277 + } 278 + 279 + $ref_list->addRef($wire_ref); 259 280 } 260 281 261 282 // TODO: Here, we have a structured list of refs. In a future change, ··· 275 296 // a little surprising, but is consistent with the native behavior of the 276 297 // protocol. 277 298 299 + // Likewise, we don't send back any capabilities if we're sending only 300 + // "shallow" frames. 301 + 278 302 $output = array(); 279 303 $is_first = true; 280 304 foreach ($refs as $ref) { 281 - if ($is_first) { 305 + $is_shallow = $ref->getIsShallow(); 306 + 307 + if ($is_shallow) { 308 + $result = sprintf( 309 + "shallow %s\n", 310 + $ref->getHash()); 311 + } else if ($is_first) { 282 312 $result = sprintf( 283 313 "%s %s\0%s\n", 284 314 $ref->getHash(),
+12 -1
src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php
··· 5 5 6 6 private $name; 7 7 private $hash; 8 + private $isShallow; 8 9 9 10 public function setName($name) { 10 11 $this->name = $name; ··· 24 25 return $this->hash; 25 26 } 26 27 28 + public function setIsShallow($is_shallow) { 29 + $this->isShallow = $is_shallow; 30 + return $this; 31 + } 32 + 33 + public function getIsShallow() { 34 + return $this->isShallow; 35 + } 36 + 27 37 public function newSortVector() { 28 38 return id(new PhutilSortVector()) 29 - ->addString($this->getName()); 39 + ->addInt((int)$this->getIsShallow()) 40 + ->addString((string)$this->getName()); 30 41 } 31 42 32 43 }