Index: src/applications/diffusion/controller/DiffusionRepositoryController.php =================================================================== --- src/applications/diffusion/controller/DiffusionRepositoryController.php +++ src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -176,38 +176,50 @@ if ($repository->isHosted()) { $ssh_uri = $repository->getSSHCloneURIObject(); if ($ssh_uri) { - $clone_uri = $this->renderCloneURI( + $clone_uri = $this->renderCloneCommand( + $repository, $ssh_uri, $repository->getServeOverSSH(), '/settings/panel/ssh/'); - $view->addProperty(pht('Clone URI (SSH)'), $clone_uri); + $view->addProperty( + $repository->isSVN() + ? pht('Checkout (SSH)') + : pht('Clone (SSH)'), + $clone_uri); } $http_uri = $repository->getHTTPCloneURIObject(); if ($http_uri) { - $clone_uri = $this->renderCloneURI( + $clone_uri = $this->renderCloneCommand( + $repository, $http_uri, $repository->getServeOverHTTP(), PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth') ? '/settings/panel/vcspassword/' : null); - $view->addProperty(pht('Clone URI (HTTP)'), $clone_uri); + $view->addProperty( + $repository->isSVN() + ? pht('Checkout (HTTP)') + : pht('Clone (HTTP)'), + $clone_uri); } } else { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $view->addProperty( - pht('Clone URI'), - $this->renderCloneURI( + pht('Clone'), + $this->renderCloneCommand( + $repository, $repository->getPublicCloneURI())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $view->addProperty( - pht('Repository Root'), - $this->renderCloneURI( + pht('Checkout'), + $this->renderCloneCommand( + $repository, $repository->getPublicCloneURI())); break; } @@ -526,7 +538,8 @@ return $browse_panel; } - private function renderCloneURI( + private function renderCloneCommand( + PhabricatorRepository $repository, $uri, $serve_mode = null, $manage_uri = null) { @@ -535,11 +548,32 @@ Javelin::initBehavior('select-on-click'); + switch ($repository->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $command = csprintf( + 'git clone %s %s', + $uri, + $repository->getCloneName()); + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: + $command = csprintf( + 'hg clone %s %s', + $uri, + $repository->getCloneName()); + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $command = csprintf( + 'svn checkout %s %s', + $uri, + $repository->getCloneName()); + break; + } + $input = javelin_tag( 'input', array( 'type' => 'text', - 'value' => (string)$uri, + 'value' => (string)$command, 'class' => 'diffusion-clone-uri', 'sigil' => 'select-on-click', 'readonly' => 'true', Index: src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php =================================================================== --- src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php +++ src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php @@ -28,6 +28,7 @@ $v_name = $repository->getName(); $v_desc = $repository->getDetail('description'); + $v_clone_name = $repository->getDetail('clone-name'); $e_name = true; $errors = array(); @@ -35,6 +36,7 @@ $v_name = $request->getStr('name'); $v_desc = $request->getStr('description'); $v_projects = $request->getArr('projectPHIDs'); + $v_clone_name = $request->getStr('cloneName'); if (!strlen($v_name)) { $e_name = pht('Required'); @@ -50,6 +52,7 @@ $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $type_edge = PhabricatorTransactions::TYPE_EDGE; + $type_clone_name = PhabricatorRepositoryTransaction::TYPE_CLONE_NAME; $xactions[] = id(clone $template) ->setTransactionType($type_name) @@ -60,6 +63,10 @@ ->setNewValue($v_desc); $xactions[] = id(clone $template) + ->setTransactionType($type_clone_name) + ->setNewValue($v_clone_name); + + $xactions[] = id(clone $template) ->setTransactionType($type_edge) ->setMetadataValue( 'edge:type', @@ -94,6 +101,15 @@ ->setValue($v_name) ->setError($e_name)) ->appendChild( + id(new AphrontFormTextControl()) + ->setName('cloneName') + ->setLabel(pht('Clone/Checkout As')) + ->setValue($v_clone_name) + ->setCaption( + pht( + 'Optional directory name to use when cloning or checking out '. + 'this repository.'))) + ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('description') ->setLabel(pht('Description')) Index: src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php =================================================================== --- src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -253,6 +253,15 @@ $view->addProperty(pht('Type'), $type); $view->addProperty(pht('Callsign'), $repository->getCallsign()); + + $clone_name = $repository->getDetail('clone-name'); + + $view->addProperty( + pht('Clone/Checkout As'), + $clone_name + ? $clone_name.'/' + : phutil_tag('em', array(), $repository->getCloneName().'/')); + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( $repository->getPHID(), PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); Index: src/applications/repository/editor/PhabricatorRepositoryEditor.php =================================================================== --- src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -31,6 +31,7 @@ $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; + $types[] = PhabricatorRepositoryTransaction::TYPE_CLONE_NAME; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -84,6 +85,8 @@ return $object->getCredentialPHID(); case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: return $object->shouldAllowDangerousChanges(); + case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: + return $object->getDetail('clone-name'); } } @@ -115,6 +118,7 @@ case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: + case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: return $xaction->getNewValue(); case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: @@ -183,6 +187,9 @@ case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: $object->setDetail('allow-dangerous-changes', $xaction->getNewValue()); return; + case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: + $object->setDetail('clone-name', $xaction->getNewValue()); + return; case PhabricatorRepositoryTransaction::TYPE_ENCODING: // Make sure the encoding is valid by converting to UTF-8. This tests // that the user has mbstring installed, and also that they didn't type @@ -294,6 +301,7 @@ case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: + case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, Index: src/applications/repository/storage/PhabricatorRepository.php =================================================================== --- src/applications/repository/storage/PhabricatorRepository.php +++ src/applications/repository/storage/PhabricatorRepository.php @@ -212,6 +212,34 @@ } + /** + * Get the name of the directory this repository should clone or checkout + * into. For example, if the repository name is "Example Repository", a + * reasonable name might be "example-repository". This is used to help users + * get reasonable results when cloning repositories, since they generally do + * not want to clone into directories called "X/" or "Example Repository/". + * + * @return string + */ + public function getCloneName() { + $name = $this->getDetail('clone-name'); + + // Make some reasonable effort to produce reasonable default directory + // names from repository names. + if (!strlen($name)) { + $name = $this->getName(); + $name = phutil_utf8_strtolower($name); + $name = preg_replace('@[/ -:]+@', '-', $name); + $name = trim($name, '-'); + if (!strlen($name)) { + $name = $this->getCallsign(); + } + } + + return $name; + } + + /* -( Remote Command Execution )------------------------------------------- */ Index: src/applications/repository/storage/PhabricatorRepositoryTransaction.php =================================================================== --- src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -23,6 +23,7 @@ const TYPE_PUSH_POLICY = 'repo:push-policy'; const TYPE_CREDENTIAL = 'repo:credential'; const TYPE_DANGEROUS = 'repo:dangerous'; + const TYPE_CLONE_NAME = 'repo:clone-name'; // TODO: Clean up these legacy transaction types. const TYPE_SSH_LOGIN = 'repo:ssh-login'; @@ -349,6 +350,23 @@ '%s enabled protection against dangerous changes.', $this->renderHandleLink($author_phid)); } + case self::TYPE_CLONE_NAME: + if (strlen($old) && !strlen($new)) { + return pht( + '%s removed the clone name of this repository.', + $this->renderHandleLink($author_phid)); + } else if (strlen($new) && !strlen($old)) { + return pht( + '%s set the clone name of this repository to "%s".', + $this->renderHandleLink($author_phid), + $new); + } else { + return pht( + '%s changed the clone name of this repository from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } } return parent::getTitle(); Index: src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php =================================================================== --- src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -228,6 +228,7 @@ private function applyInternalEffects( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { + switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_VIEW_POLICY: $object->setViewPolicy($xaction->getNewValue());