diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -262,17 +262,23 @@ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Git repository.')); + pht( + 'This repository ("%s") is not a Git repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Mercurial repository.')); + pht( + 'This repository ("%s") is not a Mercurial repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Subversion repository.')); + pht( + 'This repository ("%s") is not a Subversion repository.', + $repository->getDisplayName())); break; default: $result = new PhabricatorVCSResponse( @@ -480,7 +486,9 @@ private function getRequestDirectoryPath(PhabricatorRepository $repository) { $request = $this->getRequest(); $request_path = $request->getRequestURI()->getPath(); - $base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path); + + $info = PhabricatorRepository::parseRepositoryServicePath($request_path); + $base_path = $info['path']; // For Git repositories, strip an optional directory component if it // isn't the name of a known Git resource. This allows users to clone diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -6,6 +6,7 @@ private $repository; private $hasWriteAccess; private $proxyURI; + private $baseRequestPath; public function getRepository() { if (!$this->repository) { @@ -45,6 +46,10 @@ abstract protected function identifyRepository(); abstract protected function executeRepositoryOperations(); + protected function getBaseRequestPath() { + return $this->baseRequestPath; + } + protected function writeError($message) { $this->getErrorChannel()->write($message); return $this; @@ -149,25 +154,29 @@ protected function loadRepositoryWithPath($path) { $viewer = $this->getUser(); - $regex = '@^/?diffusion/(?P[A-Z]+)(?:/|\z)@'; - $matches = null; - if (!preg_match($regex, $path, $matches)) { + $info = PhabricatorRepository::parseRepositoryServicePath($path); + if ($info === null) { throw new Exception( pht( - 'Unrecognized repository path "%s". Expected a path like "%s".', + 'Unrecognized repository path "%s". Expected a path like "%s" '. + 'or "%s".', $path, - '/diffusion/X/')); + '/diffusion/X/', + '/diffusion/123/')); } - $callsign = $matches[1]; + $identifier = $info['identifier']; + $base = $info['base']; + + $this->baseRequestPath = $base; + $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) - ->withCallsigns(array($callsign)) + ->withIdentifiers(array($identifier)) ->executeOne(); - if (!$repository) { throw new Exception( - pht('No repository "%s" exists!', $callsign)); + pht('No repository "%s" exists!', $identifier)); } switch ($repository->getServeOverSSH()) { @@ -179,7 +188,9 @@ case PhabricatorRepository::SERVE_OFF: default: throw new Exception( - pht('This repository is not available over SSH.')); + pht( + 'This repository ("%s") is not available over SSH.', + $repository->getDisplayName())); } return $repository; diff --git a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php --- a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php @@ -377,14 +377,12 @@ $repository = $this->getRepository(); $path = $this->getPathFromSubversionURI($uri_string); - $path = preg_replace( - '(^/diffusion/[A-Z]+)', - rtrim($repository->getLocalPath(), '/'), - $path); + $external_base = $this->getBaseRequestPath(); - if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) { - $path = rtrim($path, '/'); - } + // Replace "/diffusion/X" in the request with the repository local path, + // so "/diffusion/X/master/" becomes "/path/to/repository/X/master/". + $local_path = rtrim($repository->getLocalPath(), '/'); + $path = $local_path.substr($path, strlen($external_base)); // NOTE: We are intentionally NOT removing username information from the // URI. Subversion retains it over the course of the request and considers @@ -398,7 +396,7 @@ if ($this->externalBaseURI === null) { $pre = (string)id(clone $uri)->setPath(''); - $external_path = '/diffusion/'.$repository->getCallsign(); + $external_path = $external_base; $external_path = $this->normalizeSVNPath($external_path); $this->externalBaseURI = $pre.$external_path; diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -687,6 +687,41 @@ return "/r{$callsign}{$identifier}"; } + public static function parseRepositoryServicePath($request_path) { + // NOTE: In Mercurial over SSH, the path will begin without a leading "/", + // so we're matching it optionally. + + $patterns = array( + '(^'. + '(?P/?diffusion/(?P[A-Z]+|[0-9]\d*))'. + '(?P(?:/.*)?)'. + '\z)', + ); + + $identifier = null; + foreach ($patterns as $pattern) { + $matches = null; + if (!preg_match($pattern, $request_path, $matches)) { + continue; + } + + $identifier = $matches['identifier']; + $base = $matches['base']; + $path = $matches['path']; + break; + } + + if ($identifier === null) { + return null; + } + + return array( + 'identifier' => $identifier, + 'base' => $base, + 'path' => $path, + ); + } + public function getCanonicalPath($request_path) { $standard_pattern = '(^'.