@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.

Make the "git upload-pack" proxy more robust

Summary:
Depends on D20381. Ref T8093. This makes minor improvements to the protocol proxy to handle cases where we add, remove, or replace refs and may need to move the "capabilities" section.

Rather than invoking a callback on every ref: parse the whole ref list into a data structure, mutate it if necessary (in a future diff), then dump it back into wire format.

This allows us to shift the capabilities data (which needs to be coupled with the first ref) around if we modify the first ref, and reorder the reflist alphabetically like git does.

When the server has no refs, Git sends no capabilities data. This is easy to emulate, just surprising.

Test Plan:
Tested the cases not covered by D20381:

- Fetching where the fetch actually fetches data.
- `ls-remote` when we hide the first ref (capabilities data properly moves to the first visible ref).
- `ls-remote` when the remote is empty (we just drop the capabilities frame completely).

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T8093

Differential Revision: https://secure.phabricator.com/D20436

+134 -60
+6
src/__phutil_library_map__.php
··· 815 815 'DiffusionGitUploadPackSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php', 816 816 'DiffusionGitUploadPackWireProtocol' => 'applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php', 817 817 'DiffusionGitWireProtocol' => 'applications/diffusion/protocol/DiffusionGitWireProtocol.php', 818 + 'DiffusionGitWireProtocolCapabilities' => 'applications/diffusion/protocol/DiffusionGitWireProtocolCapabilities.php', 819 + 'DiffusionGitWireProtocolRef' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRef.php', 820 + 'DiffusionGitWireProtocolRefList' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRefList.php', 818 821 'DiffusionGraphController' => 'applications/diffusion/controller/DiffusionGraphController.php', 819 822 'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php', 820 823 'DiffusionHistoryListView' => 'applications/diffusion/view/DiffusionHistoryListView.php', ··· 6464 6467 'DiffusionGitUploadPackSSHWorkflow' => 'DiffusionGitSSHWorkflow', 6465 6468 'DiffusionGitUploadPackWireProtocol' => 'DiffusionGitWireProtocol', 6466 6469 'DiffusionGitWireProtocol' => 'Phobject', 6470 + 'DiffusionGitWireProtocolCapabilities' => 'Phobject', 6471 + 'DiffusionGitWireProtocolRef' => 'Phobject', 6472 + 'DiffusionGitWireProtocolRefList' => 'Phobject', 6467 6473 'DiffusionGraphController' => 'DiffusionController', 6468 6474 'DiffusionHistoryController' => 'DiffusionController', 6469 6475 'DiffusionHistoryListView' => 'DiffusionHistoryView',
+50 -60
src/applications/diffusion/protocol/DiffusionGitUploadPackWireProtocol.php
··· 166 166 $head_key = head_key($frames); 167 167 $last_key = last_key($frames); 168 168 169 - $output = array(); 169 + $capabilities = null; 170 + $last_frame = null; 171 + 172 + $refs = array(); 170 173 foreach ($frames as $key => $frame) { 171 174 $is_last = ($key === $last_key); 172 175 if ($is_last) { 173 - $output[] = $frame; 174 176 // This is a "0000" frame at the end of the list of refs, so we pass 175 - // it through unmodified. 177 + // it through unmodified after we figure out what the rest of the 178 + // frames should look like, below. 179 + $last_frame = $frame; 176 180 continue; 177 181 } 178 182 ··· 230 234 231 235 $hash = $matches['hash']; 232 236 $name = $matches['name']; 233 - $capabilities = idx($matches, 'capabilities'); 234 237 235 - $ref = array( 238 + if ($is_first) { 239 + $capabilities = $matches['capabilities']; 240 + } 241 + 242 + $refs[] = array( 236 243 'hash' => $hash, 237 244 'name' => $name, 238 - 'capabilities' => $capabilities, 239 245 ); 246 + } 247 + 248 + $capabilities = DiffusionGitWireProtocolCapabilities::newFromWireFormat( 249 + $capabilities); 240 250 241 - $old_ref = $ref; 251 + $ref_list = id(new DiffusionGitWireProtocolRefList()) 252 + ->setCapabilities($capabilities); 253 + 254 + foreach ($refs as $ref) { 255 + $ref_list->addRef( 256 + id(new DiffusionGitWireProtocolRef()) 257 + ->setName($ref['name']) 258 + ->setHash($ref['hash'])); 259 + } 260 + 261 + // TODO: Here, we have a structured list of refs. In a future change, 262 + // we are free to mutate the structure before flattening it back into 263 + // wire format. 242 264 243 - $ref = $this->willReadRef($ref); 265 + $refs = $ref_list->getRefs(); 244 266 245 - $new_ref = $ref; 267 + // Before we write the ref list, sort it for consistency with native 268 + // Git output. We may have added, removed, or renamed refs and ended up 269 + // with an out-of-order list. 246 270 247 - $this->didRewriteRef($old_ref, $new_ref); 271 + $refs = msortv($refs, 'newSortVector'); 248 272 249 - if ($ref === null) { 250 - continue; 251 - } 273 + // The first ref we send back includes the capabilities data. Note that if 274 + // we send back no refs, we also don't send back capabilities! This is 275 + // a little surprising, but is consistent with the native behavior of the 276 + // protocol. 252 277 253 - if (isset($ref['capabilities'])) { 278 + $output = array(); 279 + $is_first = true; 280 + foreach ($refs as $ref) { 281 + if ($is_first) { 254 282 $result = sprintf( 255 283 "%s %s\0%s\n", 256 - $ref['hash'], 257 - $ref['name'], 258 - $ref['capabilities']); 284 + $ref->getHash(), 285 + $ref->getName(), 286 + $ref_list->getCapabilities()->toWireFormat()); 259 287 } else { 260 288 $result = sprintf( 261 289 "%s %s\n", 262 - $ref['hash'], 263 - $ref['name']); 290 + $ref->getHash(), 291 + $ref->getName()); 264 292 } 265 293 266 294 $output[] = $this->newProtocolFrame('data', $result); 295 + $is_first = false; 267 296 } 297 + 298 + $output[] = $last_frame; 268 299 269 300 return $this->newProtocolDataMessage($output); 270 301 } ··· 289 320 $message = implode('', $message); 290 321 291 322 return $message; 292 - } 293 - 294 - private function willReadRef(array $ref) { 295 - return $ref; 296 - } 297 - 298 - private function didRewriteRef($old_ref, $new_ref) { 299 - $log = $this->getProtocolLog(); 300 - if (!$log) { 301 - return; 302 - } 303 - 304 - if (!$old_ref) { 305 - $old_name = null; 306 - } else { 307 - $old_name = $old_ref['name']; 308 - } 309 - 310 - if (!$new_ref) { 311 - $new_name = null; 312 - } else { 313 - $new_name = $new_ref['name']; 314 - } 315 - 316 - if ($old_name === $new_name) { 317 - return; 318 - } 319 - 320 - if ($old_name === null) { 321 - $old_name = '<null>'; 322 - } 323 - 324 - if ($new_name === null) { 325 - $new_name = '<null>'; 326 - } 327 - 328 - $log->didWriteFrame( 329 - pht( 330 - 'Rewrite Ref: %s -> %s', 331 - $old_name, 332 - $new_name)); 333 323 } 334 324 335 325 }
+18
src/applications/diffusion/protocol/DiffusionGitWireProtocolCapabilities.php
··· 1 + <?php 2 + 3 + final class DiffusionGitWireProtocolCapabilities 4 + extends Phobject { 5 + 6 + private $raw; 7 + 8 + public static function newFromWireFormat($raw) { 9 + $capabilities = new self(); 10 + $capabilities->raw = $raw; 11 + return $capabilities; 12 + } 13 + 14 + public function toWireFormat() { 15 + return $this->raw; 16 + } 17 + 18 + }
+32
src/applications/diffusion/protocol/DiffusionGitWireProtocolRef.php
··· 1 + <?php 2 + 3 + final class DiffusionGitWireProtocolRef 4 + extends Phobject { 5 + 6 + private $name; 7 + private $hash; 8 + 9 + public function setName($name) { 10 + $this->name = $name; 11 + return $this; 12 + } 13 + 14 + public function getName() { 15 + return $this->name; 16 + } 17 + 18 + public function setHash($hash) { 19 + $this->hash = $hash; 20 + return $this; 21 + } 22 + 23 + public function getHash() { 24 + return $this->hash; 25 + } 26 + 27 + public function newSortVector() { 28 + return id(new PhutilSortVector()) 29 + ->addString($this->getName()); 30 + } 31 + 32 + }
+28
src/applications/diffusion/protocol/DiffusionGitWireProtocolRefList.php
··· 1 + <?php 2 + 3 + final class DiffusionGitWireProtocolRefList 4 + extends Phobject { 5 + 6 + private $capabilities; 7 + private $refs = array(); 8 + 9 + public function setCapabilities( 10 + DiffusionGitWireProtocolCapabilities $capabilities) { 11 + $this->capabilities = $capabilities; 12 + return $this; 13 + } 14 + 15 + public function getCapabilities() { 16 + return $this->capabilities; 17 + } 18 + 19 + public function addRef(DiffusionGitWireProtocolRef $ref) { 20 + $this->refs[] = $ref; 21 + return $this; 22 + } 23 + 24 + public function getRefs() { 25 + return $this->refs; 26 + } 27 + 28 + }