Index: src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php =================================================================== --- src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php +++ src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php @@ -35,6 +35,7 @@ $repository->writeStatusMessage( PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY); + $this->waitForGitClient(); } return $err; Index: src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php =================================================================== --- src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php +++ src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php @@ -21,10 +21,16 @@ $future = new ExecFuture('git-upload-pack %s', $repository->getLocalPath()); - return $this->newPassthruCommand() + $err = $this->newPassthruCommand() ->setIOChannel($this->getIOChannel()) ->setCommandChannelFromExecFuture($future) ->execute(); + + if (!$err) { + $this->waitForGitClient(); + } + + return $err; } } Index: src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php =================================================================== --- src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php +++ src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php @@ -7,4 +7,17 @@ return parent::writeError($message."\n"); } + protected function waitForGitClient() { + $io_channel = $this->getIOChannel(); + + // If we don't wait for the client to close the connection, `git` will + // consider it an early abort and fail. Sit around until Git is comfortable + // that it really received all the data. + while ($io_channel->isOpenForReading()) { + $io_channel->update(); + $this->getErrorChannel()->flush(); + PhutilChannel::waitForAny(array($io_channel)); + } + } + }