diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index be950a46d7..155a4040ed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -506,6 +506,7 @@ phutil_register_library_map(array( 'DiffusionRepositoryCreateController' => 'applications/diffusion/controller/DiffusionRepositoryCreateController.php', 'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php', 'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php', + 'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php', 'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php', 'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php', 'DiffusionRepositoryEditPolicyController' => 'applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php', @@ -2677,6 +2678,7 @@ phutil_register_library_map(array( 'DiffusionRepositoryCreateController' => 'DiffusionController', 'DiffusionRepositoryEditActivateController' => 'DiffusionController', 'DiffusionRepositoryEditBasicController' => 'DiffusionController', + 'DiffusionRepositoryEditBranchesController' => 'DiffusionController', 'DiffusionRepositoryEditController' => 'DiffusionController', 'DiffusionRepositoryEditEncodingController' => 'DiffusionController', 'DiffusionRepositoryEditPolicyController' => 'DiffusionController', diff --git a/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php b/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php index 5ef260b9cf..806c748aa2 100644 --- a/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php +++ b/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php @@ -69,6 +69,7 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication { 'encoding/' => 'DiffusionRepositoryEditEncodingController', 'activate/' => 'DiffusionRepositoryEditActivateController', 'policy/' => 'DiffusionRepositoryEditPolicyController', + 'branches/' => 'DiffusionRepositoryEditBranchesController', ), ), 'inline/' => array( diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php new file mode 100644 index 0000000000..c177d13fed --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php @@ -0,0 +1,147 @@ +getRequest(); + $viewer = $request->getUser(); + $drequest = $this->diffusionRequest; + $repository = $drequest->getRepository(); + + $repository = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->withIDs(array($repository->getID())) + ->executeOne(); + + if (!$repository) { + return new Aphront404Response(); + } + + switch ($repository->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + throw new Exception( + pht('Subversion does not support branches!')); + default: + throw new Exception( + pht('Repository has unknown version control system!')); + } + + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); + + $v_default = $repository->getHumanReadableDetail('default-branch'); + $v_track = $repository->getHumanReadableDetail('branch-filter'); + $v_autoclose = $repository->getHumanReadableDetail('close-commits-filter'); + + if ($request->isFormPost()) { + $v_default = $request->getStr('default'); + $v_track = $request->getStrList('track'); + $v_autoclose = $request->getStrList('autoclose'); + + $xactions = array(); + $template = id(new PhabricatorRepositoryTransaction()); + + $type_default = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH; + $type_track = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY; + $type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY; + + $xactions[] = id(clone $template) + ->setTransactionType($type_default) + ->setNewValue($v_default); + + $xactions[] = id(clone $template) + ->setTransactionType($type_track) + ->setNewValue($v_track); + + $xactions[] = id(clone $template) + ->setTransactionType($type_autoclose) + ->setNewValue($v_autoclose); + + id(new PhabricatorRepositoryEditor()) + ->setContinueOnNoEffect(true) + ->setContentSourceFromRequest($request) + ->setActor($viewer) + ->applyTransactions($repository, $xactions); + + return id(new AphrontRedirectResponse())->setURI($edit_uri); + } + + $content = array(); + + $crumbs = $this->buildCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Edit Branches'))); + + $title = pht('Edit Branches (%s)', $repository->getName()); + + $policies = id(new PhabricatorPolicyQuery()) + ->setViewer($viewer) + ->setObject($repository) + ->execute(); + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendRemarkupInstructions( + pht( + 'You can choose a **Default Branch** for viewing this repository.'. + "\n\n". + 'If you want to import only some branches into Diffusion, you can '. + 'list them in **Track Only**. Other branches will be ignored. If '. + 'you do not specify any branches, all branches are tracked.'. + "\n\n". + 'If you have **Autoclose** enabled, Phabricator can close tasks and '. + 'revisions when corresponding commits are pushed to the repository. '. + 'If you want to autoclose objects only when commits appear on '. + 'specific branches, you can list those branches in **Autoclose '. + 'Only**. By default, all branches autoclose objects.')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('default') + ->setLabel(pht('Default Branch')) + ->setValue($v_default) + ->setCaption( + pht('Example: %s', phutil_tag('tt', array(), 'develop')))) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('track') + ->setLabel(pht('Track Only')) + ->setValue($v_track) + ->setCaption( + pht('Example: %s', phutil_tag('tt', array(), 'master, develop')))) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('autoclose') + ->setLabel(pht('Autoclose Only')) + ->setValue($v_autoclose) + ->setCaption( + pht('Example: %s', phutil_tag('tt', array(), 'master, release')))) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Save Branches')) + ->addCancelButton($edit_uri)); + + $form_box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->setForm($form); + + return $this->buildApplicationPage( + array( + $crumbs, + $form_box, + ), + array( + 'title' => $title, + 'device' => true, + )); + } + +} diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php index 9915da501e..e43590d422 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php @@ -8,6 +8,23 @@ final class DiffusionRepositoryEditController extends DiffusionController { $drequest = $this->diffusionRequest; $repository = $drequest->getRepository(); + $is_svn = false; + $is_git = false; + $is_hg = false; + switch ($repository->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $is_git = true; + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $is_svn = true; + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: + $is_hg = true; + break; + } + + $has_branches = ($is_git || $is_hg); + $crumbs = $this->buildCrumbs(); $crumbs->addCrumb( id(new PhabricatorCrumbView()) @@ -37,6 +54,13 @@ final class DiffusionRepositoryEditController extends DiffusionController { $encoding_properties = $this->buildEncodingProperties($repository, $encoding_actions); + $branches_properties = null; + if ($has_branches) { + $branches_properties = $this->buildBranchesProperties( + $repository, + $this->buildBranchesActions($repository)); + } + $xactions = id(new PhabricatorRepositoryTransactionQuery()) ->setViewer($user) ->withObjectPHIDs(array($repository->getPHID())) @@ -65,6 +89,10 @@ final class DiffusionRepositoryEditController extends DiffusionController { ->addPropertyList($policy_properties) ->addPropertyList($encoding_properties); + if ($branches_properties) { + $obj_box->addPropertyList($branches_properties); + } + return $this->buildApplicationPage( array( $crumbs, @@ -249,4 +277,58 @@ final class DiffusionRepositoryEditController extends DiffusionController { return $view; } + private function buildBranchesActions(PhabricatorRepository $repository) { + $viewer = $this->getRequest()->getUser(); + + $view = id(new PhabricatorActionListView()) + ->setObjectURI($this->getRequest()->getRequestURI()) + ->setUser($viewer); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $edit = id(new PhabricatorActionView()) + ->setIcon('edit') + ->setName(pht('Edit Branches')) + ->setHref( + $this->getRepositoryControllerURI($repository, 'edit/branches/')) + ->setWorkflow(!$can_edit) + ->setDisabled(!$can_edit); + $view->addAction($edit); + + return $view; + } + + private function buildBranchesProperties( + PhabricatorRepository $repository, + PhabricatorActionListView $actions) { + + $viewer = $this->getRequest()->getUser(); + + $view = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setActionList($actions) + ->addSectionHeader(pht('Branches')); + + $default_branch = nonempty( + $repository->getHumanReadableDetail('default-branch'), + phutil_tag('em', array(), $repository->getDefaultBranch())); + $view->addProperty(pht('Default Branch'), $default_branch); + + $track_only = nonempty( + $repository->getHumanReadableDetail('branch-filter'), + phutil_tag('em', array(), pht('Track All Branches'))); + $view->addProperty(pht('Track Only'), $track_only); + + $autoclose_only = nonempty( + $repository->getHumanReadableDetail('close-commits-filter'), + phutil_tag('em', array(), pht('Autoclose On All Branches'))); + $view->addProperty(pht('Autoclose Only'), $autoclose_only); + + return $view; + } + + } diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index bd0561d43c..b0c4298999 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -10,6 +10,9 @@ final class PhabricatorRepositoryEditor $types[] = PhabricatorRepositoryTransaction::TYPE_NAME; $types[] = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $types[] = PhabricatorRepositoryTransaction::TYPE_ENCODING; + $types[] = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH; + $types[] = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY; + $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -29,6 +32,12 @@ final class PhabricatorRepositoryEditor return $object->getDetail('description'); case PhabricatorRepositoryTransaction::TYPE_ENCODING: return $object->getDetail('encoding'); + case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: + return $object->getDetail('default-branch'); + case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: + return array_keys($object->getDetail('branch-filter', array())); + case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: + return array_keys($object->getDetail('close-commits-filter', array())); } } @@ -41,6 +50,9 @@ final class PhabricatorRepositoryEditor case PhabricatorRepositoryTransaction::TYPE_NAME: case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: case PhabricatorRepositoryTransaction::TYPE_ENCODING: + case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: + case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: + case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: return $xaction->getNewValue(); } } @@ -59,6 +71,19 @@ final class PhabricatorRepositoryEditor case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: $object->setDetail('description', $xaction->getNewValue()); break; + case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: + $object->setDetail('default-branch', $xaction->getNewValue()); + break; + case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: + $object->setDetail( + 'branch-filter', + array_fill_keys($xaction->getNewValue(), true)); + break; + case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: + $object->setDetail( + 'close-commits-filter', + array_fill_keys($xaction->getNewValue(), true)); + break; 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 diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 29f082105f..c395c0f1a7 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -71,6 +71,20 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return idx($this->details, $key, $default); } + public function getHumanReadableDetail($key, $default = null) { + $value = $this->getDetail($key, $default); + + switch ($key) { + case 'branch-filter': + case 'close-commits-filter': + $value = array_keys($value); + $value = implode(', ', $value); + break; + } + + return $value; + } + public function setDetail($key, $value) { $this->details[$key] = $value; return $this; diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index ab0ce8b188..6787f8b87a 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -7,6 +7,9 @@ final class PhabricatorRepositoryTransaction const TYPE_NAME = 'repo:name'; const TYPE_DESCRIPTION = 'repo:description'; const TYPE_ENCODING = 'repo:encoding'; + const TYPE_DEFAULT_BRANCH = 'repo:default-branch'; + const TYPE_TRACK_ONLY = 'repo:track-only'; + const TYPE_AUTOCLOSE_ONLY = 'repo:autoclose-only'; public function getApplicationName() { return 'repository'; @@ -65,6 +68,61 @@ final class PhabricatorRepositoryTransaction $old, $new); } + case self::TYPE_DEFAULT_BRANCH: + if (!strlen($new)) { + return pht( + '%s removed "%s" as the default branch.', + $this->renderHandleLink($author_phid), + $old); + } else if (!strlen($old)) { + return pht( + '%s set the default branch to "%s".', + $this->renderHandleLink($author_phid), + $new); + } else { + return pht( + '%s changed the default branch from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + break; + case self::TYPE_TRACK_ONLY: + if (!$new) { + return pht( + '%s set this repository to track all branches.', + $this->renderHandleLink($author_phid)); + } else if (!$old) { + return pht( + '%s set this repository to track branches: %s.', + $this->renderHandleLink($author_phid), + implode(', ', $new)); + } else { + return pht( + '%s changed track branches from "%s" to "%s".', + $this->renderHandleLink($author_phid), + implode(', ', $old), + implode(', ', $new)); + } + break; + case self::TYPE_AUTOCLOSE_ONLY: + if (!$new) { + return pht( + '%s set this repository to autoclose on all branches.', + $this->renderHandleLink($author_phid)); + } else if (!$old) { + return pht( + '%s set this repository to autoclose on branches: %s.', + $this->renderHandleLink($author_phid), + implode(', ', $new)); + } else { + return pht( + '%s changed autoclose branches from "%s" to "%s".', + $this->renderHandleLink($author_phid), + implode(', ', $old), + implode(', ', $new)); + } + break; } return parent::getTitle();