Colin Watson has proposed merging 
lp:~cjwatson/launchpad/codeimport-git-configure-code into lp:launchpad with 
lp:~cjwatson/launchpad/codeimport-git-refactor-name-validation as a 
prerequisite.

Commit message:
Extend Product:+configure-code to be able to create Git-to-Git code imports.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/codeimport-git-configure-code/+merge/308577
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
lp:~cjwatson/launchpad/codeimport-git-configure-code into lp:launchpad.
=== modified file 'lib/lp/code/javascript/productseries-setbranch.js'
--- lib/lp/code/javascript/productseries-setbranch.js	2015-06-25 07:39:40 +0000
+++ lib/lp/code/javascript/productseries-setbranch.js	2016-10-15 14:08:56 +0000
@@ -97,10 +97,9 @@
             }
         }
         // Linked
-        module.set_enabled('field.branch_location', type === 'link-lp-bzr');
-        module.set_enabled('field.branch_name', type !== 'link-lp-bzr');
-        module.set_enabled('field.branch_owner', type !== 'link-lp-bzr');
-        // New, empty branch.
+        module.set_enabled('field.branch_location', type === 'link-lp');
+        module.set_enabled('field.branch_name', type !== 'link-lp');
+        module.set_enabled('field.branch_owner', type !== 'link-lp');
         // Import
         var is_external = (type === 'import-external');
         module.set_enabled('field.repo_url', is_external);
@@ -113,6 +112,27 @@
         }
     };
 
+    module.onclick_git_repository_type = function(e) {
+        /* Which Git repository type radio button was selected? */
+        var types = document.getElementsByName('field.git_repository_type');
+        var type = 'None';
+        var i;
+        for (i = 0; i < types.length; i++) {
+            if (types[i].checked) {
+                type = types[i].value;
+                break;
+            }
+        }
+        // Linked
+        module.set_enabled(
+            'field.git_repository_location', type === 'link-lp');
+        module.set_enabled('field.git_repository_name', type !== 'link-lp');
+        module.set_enabled('field.git_repository_owner', type !== 'link-lp');
+        // Import
+        var is_external = (type === 'import-external');
+        module.set_enabled('field.git_repository_url', is_external);
+    };
+
     module.setup = function() {
         Y.all('input[name="field.rcs_type"]').on(
             'click', module.onclick_rcs_type);
@@ -120,6 +140,8 @@
             'click', module.onclick_branch_type);
         Y.all('input[name="field.default_vcs"]').on(
             'click', module.onclick_default_vcs);
+        Y.all('input[name="field.git_repository_type"]').on(
+            'click', module.onclick_git_repository_type);
 
         // Set the initial state.
         module.onclick_rcs_type();
@@ -128,6 +150,7 @@
         if (document.getElementById('default_vcs')) {
             module.setup_expanders();
             module.onclick_default_vcs();
+            module.onclick_git_repository_type();
         }
     };
 

=== modified file 'lib/lp/code/javascript/tests/test_productseries-setbranch.html'
--- lib/lp/code/javascript/tests/test_productseries-setbranch.html	2012-10-26 09:54:28 +0000
+++ lib/lp/code/javascript/tests/test_productseries-setbranch.html	2016-10-15 14:08:56 +0000
@@ -61,7 +61,7 @@
                   <label>
                     <input class="radioType" checked="checked"
                            id="field.branch_type.0"
-                           name="field.branch_type" type="radio" value="link-lp-bzr" />
+                           name="field.branch_type" type="radio" value="link-lp" />
                     Link to a Bazaar branch already in Launchpad
                   </label>
                     <table>

=== modified file 'lib/lp/code/templates/configure-code.pt'
--- lib/lp/code/templates/configure-code.pt	2016-04-28 02:09:21 +0000
+++ lib/lp/code/templates/configure-code.pt	2016-10-15 14:08:56 +0000
@@ -51,10 +51,10 @@
                 <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
               </div>
 
+              <h3>Link or import an existing branch</h3>
               <table id="form_bzr" class="form">
                 <tr>
                   <td>
-                    <h3>Link or import an existing branch</h3>
                     <label tal:replace="structure view/branch_type_link">
                       Link to a Bazaar branch already on Launchpad
                     </label>
@@ -133,20 +133,47 @@
                 <span class="sprite gitbranch">Git settings</span>
               </a>
               <div id="git-expander-content">
-                <div id="form_git" class="form">
-                  <div class="push-instructions">
-                    <div metal:use-macro="context/@@+configure-code-macros/push-instructions-git"></div>
-                    <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
-                  </div>
-
-                  <h3>Link an existing repository</h3>
-                  <p>Link to an existing Git repository already on Launchpad</p>
-                  <table>
-                    <tal:widget define="widget nocall:view/widgets/git_repository_location">
-                      <metal:block use-macro="context/@@launchpad_form/widget_row" />
-                    </tal:widget>
-                  </table>
+                <div class="push-instructions">
+                  <div metal:use-macro="context/@@+configure-code-macros/push-instructions-git"></div>
+                  <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
                 </div>
+
+                <h3>Link or import an existing repository</h3>
+                <table id="form_git" class="form">
+                  <tr>
+                    <td>
+                      <label tal:replace="structure view/git_repository_type_link">
+                        Link to a Git repository already on Launchpad
+                      </label>
+                      <table class="subordinate">
+                        <tal:widget define="widget nocall:view/widgets/git_repository_location">
+                          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                        </tal:widget>
+                      </table>
+                    </td>
+                  </tr>
+
+                  <tr id="git_mirror"
+                      tal:condition="request/features/code.import.git_target">
+                    <td>
+                      <label tal:replace="structure view/git_repository_type_import">
+                        Import a repository hosted somewhere else
+                      </label>
+                      <table class="subordinate">
+                        <tal:widget define="widget nocall:view/widgets/git_repository_name">
+                          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                        </tal:widget>
+                        <tal:widget define="widget nocall:view/widgets/git_repository_owner">
+                          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                        </tal:widget>
+
+                        <tal:widget define="widget nocall:view/widgets/git_repository_url">
+                          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                        </tal:widget>
+                      </table>
+                    </td>
+                  </tr>
+                </table>
               </div>
             </div>
           </tal:block>

=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2016-10-15 14:08:56 +0000
+++ lib/lp/registry/browser/product.py	2016-10-15 14:08:56 +0000
@@ -151,14 +151,20 @@
     RevisionControlSystems,
     TargetRevisionControlSystems,
     )
-from lp.code.errors import BranchExists
+from lp.code.errors import (
+    BranchExists,
+    GitRepositoryExists,
+    )
 from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.branchjob import IRosettaUploadJobSource
 from lp.code.interfaces.codeimport import (
     ICodeImport,
     ICodeImportSet,
     )
-from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.code.interfaces.gitrepository import (
+    IGitRepository,
+    IGitRepositorySet,
+    )
 from lp.registry.browser import (
     add_subscribe_link,
     BaseRdfView,
@@ -1658,18 +1664,26 @@
         return BatchNavigator(decorated_result, self.request)
 
 
-LINK_LP_BZR = 'link-lp-bzr'
+LINK_LP = 'link-lp'
 IMPORT_EXTERNAL = 'import-external'
 
 
 BRANCH_TYPE_VOCABULARY = SimpleVocabulary((
-    SimpleTerm(LINK_LP_BZR, LINK_LP_BZR,
+    SimpleTerm(LINK_LP, LINK_LP,
                _("Link to a Bazaar branch already on Launchpad")),
     SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,
                _("Import a branch hosted somewhere else")),
     ))
 
 
+GIT_REPOSITORY_TYPE_VOCABULARY = SimpleVocabulary((
+    SimpleTerm(LINK_LP, LINK_LP,
+               _("Link to a Git repository already on Launchpad")),
+    SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,
+               _("Import a repository hosted somewhere else")),
+    ))
+
+
 class SetBranchForm(Interface):
     """The fields presented on the form for setting a branch."""
 
@@ -1706,23 +1720,37 @@
         IBranch['owner'], __name__='branch_owner', title=_('Branch owner'),
         description=_(''), required=True)
 
-
-def create_git_fields():
-    return form.Fields(
-        Choice(__name__='default_vcs',
-               title=_("Project VCS"),
-               required=True, vocabulary=VCSType,
-               description=_("The version control system for "
-                             "this project.")),
-        Choice(__name__='git_repository_location',
-               title=_('Git repository'),
-               required=False,
-               vocabulary='GitRepositoryRestrictedOnProduct',
-               description=_(
-                   "The Git repository for this project in Launchpad, "
-                   "if one exists, in the form: "
-                   "~user/project-name/+git/repo-name"))
-    )
+    default_vcs = Choice(
+        title=_("Project VCS"), required=True, vocabulary=VCSType,
+        description=_("The version control system for this project."))
+
+    git_repository_location = Choice(
+        title=_('Git repository'), required=False,
+        vocabulary='GitRepositoryRestrictedOnProduct',
+        description=_(
+            "The Git repository for this project in Launchpad, "
+            "if one exists, in the form: "
+            "~user/project-name/+git/repo-name"))
+
+    git_repository_type = Choice(
+        title=_('Import type'), required=True,
+        vocabulary=GIT_REPOSITORY_TYPE_VOCABULARY,
+        description=_('The type of import'))
+
+    git_repository_name = copy_field(
+        IGitRepository['name'], __name__='git_repository_name',
+        title=_('Repository name'), description=_(''), required=True)
+
+    git_repository_owner = copy_field(
+        IGitRepository['owner'], __name__='git_repository_owner',
+        title=_('Repository owner'), description=_(''), required=True)
+
+    git_repository_url = URIField(
+        title=_('Repository URL'), required=True,
+        description=_('The URL of the Git repository.'),
+        allowed_schemes=["git", "http", "https"],
+        allow_userinfo=True, allow_port=True, allow_query=False,
+        allow_fragment=False, trailing_slash=False)
 
 
 class ProductSetBranchView(ReturnToReferrerMixin, LaunchpadFormView,
@@ -1740,6 +1768,7 @@
     custom_widget('rcs_type', LaunchpadRadioWidget)
     custom_widget('branch_type', LaunchpadRadioWidget)
     custom_widget('default_vcs', LaunchpadRadioWidget)
+    custom_widget('git_repository_type', LaunchpadRadioWidget)
 
     errors_in_action = False
     is_series = False
@@ -1754,8 +1783,9 @@
         return dict(
             rcs_type=RevisionControlSystems.BZR,
             default_vcs=(self.context.pillar.inferred_vcs or VCSType.BZR),
-            branch_type=LINK_LP_BZR,
+            branch_type=LINK_LP,
             branch_location=self.series.branch,
+            git_repository_type=LINK_LP,
             git_repository_location=repository_set.getDefaultRepository(
                 self.context.pillar))
 
@@ -1781,8 +1811,11 @@
     def setUpFields(self):
         """See `LaunchpadFormView`."""
         super(ProductSetBranchView, self).setUpFields()
-        if not self.is_series:
-            self.form_fields = (self.form_fields + create_git_fields())
+        if self.is_series:
+            self.form_fields = self.form_fields.omit(
+                'default_vcs', 'git_repository_location',
+                'git_repository_type', 'git_repository_name',
+                'git_repository_owner', 'git_repository_url')
 
     def setUpWidgets(self):
         """See `LaunchpadFormView`."""
@@ -1803,11 +1836,9 @@
         widget = self.widgets['branch_type']
         current_value = widget._getFormValue()
         vocab = widget.vocabulary
-
-        (self.branch_type_link,
-         self.branch_type_import) = [
+        self.branch_type_link, self.branch_type_import = [
             render_radio_widget_part(widget, value, current_value)
-            for value in (LINK_LP_BZR, IMPORT_EXTERNAL)]
+            for value in (LINK_LP, IMPORT_EXTERNAL)]
 
         if not self.is_series:
             widget = self.widgets['default_vcs']
@@ -1818,14 +1849,21 @@
             self.default_vcs_bzr = render_radio_widget_part(
                 widget, vocab.BZR, current_value, 'Bazaar')
 
+            widget = self.widgets['git_repository_type']
+            current_value = widget._getFormValue()
+            vocab = widget.vocabulary
+            self.git_repository_type_link, self.git_repository_type_import = [
+                render_radio_widget_part(widget, value, current_value)
+                for value in (LINK_LP, IMPORT_EXTERNAL)]
+
     def _validateLinkLpBzr(self, data):
-        """Validate data for link-lp-bzr case."""
+        """Validate data for link-lp bzr case."""
         if 'branch_location' not in data:
             self.setFieldError(
                 'branch_location', 'The branch location must be set.')
 
     def _validateLinkLpGit(self, data):
-        """Validate data for link-lp-git case."""
+        """Validate data for link-lp git case."""
         if data.get('git_repository_location'):
             repo = data.get('git_repository_location')
             if not repo:
@@ -1833,8 +1871,8 @@
                     'git_repository_location',
                     'The repository does not exist.')
 
-    def _validateImportExternal(self, data):
-        """Validate data for import external case."""
+    def _validateImportExternalBzr(self, data):
+        """Validate data for import-external bzr case."""
         rcs_type = data.get('rcs_type')
         repo_url = data.get('repo_url')
 
@@ -1864,15 +1902,41 @@
         elif rcs_type == RevisionControlSystems.CVS:
             if 'cvs_module' not in data:
                 self.setFieldError('cvs_module', 'The CVS module must be set.')
-        self._validateBranch(data)
 
-    def _validateBranch(self, data):
-        """Validate that branch name and owner are set."""
         if 'branch_name' not in data:
             self.setFieldError('branch_name', 'The branch name must be set.')
         if 'branch_owner' not in data:
             self.setFieldError('branch_owner', 'The branch owner must be set.')
 
+    def _validateImportExternalGit(self, data):
+        """Validate data for import-external git case."""
+        git_repository_url = data.get('git_repository_url')
+
+        # Private teams are forbidden from owning code imports.
+        git_repository_owner = data.get('git_repository_owner')
+        if git_repository_owner is not None and git_repository_owner.private:
+            self.setFieldError(
+                'git_repository_owner',
+                'Private teams are forbidden from owning external imports.')
+
+        if git_repository_url is None:
+            self.setFieldError(
+                'git_repository_url',
+                'You must set the external repository URL.')
+        else:
+            reason = validate_import_url(
+                git_repository_url, RevisionControlSystems.GIT,
+                TargetRevisionControlSystems.GIT)
+            if reason:
+                self.setFieldError('git_repository_url', reason)
+
+        if 'git_repository_name' not in data:
+            self.setFieldError(
+                'git_repository_name', 'The repository name must be set.')
+        if 'git_repository_owner' not in data:
+            self.setFieldError(
+                'git_repository_owner', 'The repository owner must be set.')
+
     def _setRequired(self, names, value):
         """Mark the widget field as optional."""
         for name in names:
@@ -1897,11 +1961,25 @@
 
     def validate_widgets(self, data, names=None):
         """See `LaunchpadFormView`."""
-        names = ['branch_type', 'rcs_type', 'default_vcs']
+        names = [
+            'branch_type', 'rcs_type', 'default_vcs', 'git_repository_type']
         super(ProductSetBranchView, self).validate_widgets(data, names)
+
+        if not self.is_series:
+            git_repository_type = data.get('git_repository_type')
+            if git_repository_type == LINK_LP:
+                # Mark other widgets as non-required.
+                self._setRequired(['git_repository_url', 'git_repository_name',
+                                   'git_repository_owner'], False)
+            elif git_repository_type == IMPORT_EXTERNAL:
+                # The repository location is not required for validation.
+                self._setRequired(['git_repository_location'], False)
+            else:
+                raise AssertionError(
+                    "Unknown Git repository type %s" % git_repository_type)
+
         branch_type = data.get('branch_type')
-
-        if branch_type == LINK_LP_BZR:
+        if branch_type == LINK_LP:
             # Mark other widgets as non-required.
             self._setRequired(['rcs_type', 'repo_url', 'cvs_module',
                                'branch_name', 'branch_owner'], False)
@@ -1918,6 +1996,7 @@
                 self._setRequired(['cvs_module'], True)
         else:
             raise AssertionError("Unknown branch type %s" % branch_type)
+
         # Perform full validation now.
         super(ProductSetBranchView, self).validate_widgets(data)
 
@@ -1927,13 +2006,22 @@
         # continue as we'd likely just override the errors reported there.
         if len(self.errors) > 0:
             return
+
+        if not self.is_series:
+            git_repository_type = data.get('git_repository_type')
+            if git_repository_type == LINK_LP:
+                self._validateLinkLpGit(data)
+            elif git_repository_type == IMPORT_EXTERNAL:
+                self._validateImportExternalGit(data)
+            else:
+                raise AssertionError(
+                    "Unknown Git repository type %s" % git_repository_type)
+
         branch_type = data.get('branch_type')
-        if not self.is_series:
-            self._validateLinkLpGit(data)
-        if branch_type == IMPORT_EXTERNAL:
-            self._validateImportExternal(data)
-        elif branch_type == LINK_LP_BZR:
+        if branch_type == LINK_LP:
             self._validateLinkLpBzr(data)
+        elif branch_type == IMPORT_EXTERNAL:
+            self._validateImportExternalBzr(data)
         else:
             raise AssertionError("Unknown branch type %s" % branch_type)
 
@@ -1959,54 +2047,80 @@
             if default_vcs:
                 self.context.vcs = default_vcs
 
-            repo = data.get('git_repository_location')
-            getUtility(IGitRepositorySet).setDefaultRepository(
-                self.context, repo)
-        if branch_type == LINK_LP_BZR:
+            git_repository_type = data.get('git_repository_type')
+
+            if git_repository_type == LINK_LP:
+                repo = data.get('git_repository_location')
+                repository_set = getUtility(IGitRepositorySet)
+                if repository_set.getDefaultRepository(self.context) != repo:
+                    repository_set.setDefaultRepository(self.context, repo)
+                    self.add_update_notification()
+            elif git_repository_type == IMPORT_EXTERNAL:
+                name = data.get('git_repository_name')
+                owner = data.get('git_repository_owner')
+                url = data.get('git_repository_url')
+                try:
+                    code_import = getUtility(ICodeImportSet).new(
+                        owner=owner,
+                        registrant=self.user,
+                        context=self.context,
+                        branch_name=name,
+                        rcs_type=RevisionControlSystems.GIT,
+                        target_rcs_type=TargetRevisionControlSystems.GIT,
+                        url=url)
+                except GitRepositoryExists as e:
+                    self._setBranchExists(
+                        e.existing_repository, 'git_repository_name')
+                    self.abort_update()
+                    return
+                getUtility(IGitRepositorySet).setDefaultRepository(
+                    self.context, code_import.git_repository)
+                self.request.response.addInfoNotification(
+                    'Code import created and repository set as default.')
+            else:
+                raise UnexpectedFormData(git_repository_type)
+
+        if branch_type == LINK_LP:
             branch_location = data.get('branch_location')
             if branch_location != self.series.branch:
                 self.series.branch = branch_location
                 # Request an initial upload of translation files.
                 getUtility(IRosettaUploadJobSource).create(
                     self.series.branch, NULL_REVISION)
-            else:
-                self.series.branch = branch_location
-            self.add_update_notification()
-        else:
+                self.add_update_notification()
+        elif branch_type == IMPORT_EXTERNAL:
             branch_name = data.get('branch_name')
             branch_owner = data.get('branch_owner')
-
-            if branch_type == IMPORT_EXTERNAL:
-                rcs_type = data.get('rcs_type')
-                if rcs_type == RevisionControlSystems.CVS:
-                    cvs_root = data.get('repo_url')
-                    cvs_module = data.get('cvs_module')
-                    url = None
-                else:
-                    cvs_root = None
-                    cvs_module = None
-                    url = data.get('repo_url')
-                rcs_item = RevisionControlSystems.items[rcs_type.name]
-                try:
-                    code_import = getUtility(ICodeImportSet).new(
-                        owner=branch_owner,
-                        registrant=self.user,
-                        context=self.context,
-                        branch_name=branch_name,
-                        rcs_type=rcs_item,
-                        target_rcs_type=TargetRevisionControlSystems.BZR,
-                        url=url,
-                        cvs_root=cvs_root,
-                        cvs_module=cvs_module)
-                except BranchExists as e:
-                    self._setBranchExists(e.existing_branch, 'branch_name')
-                    self.abort_update()
-                    return
-                self.series.branch = code_import.branch
-                self.request.response.addInfoNotification(
-                    'Code import created and branch linked to the series.')
+            rcs_type = data.get('rcs_type')
+            if rcs_type == RevisionControlSystems.CVS:
+                cvs_root = data.get('repo_url')
+                cvs_module = data.get('cvs_module')
+                url = None
             else:
-                raise UnexpectedFormData(branch_type)
+                cvs_root = None
+                cvs_module = None
+                url = data.get('repo_url')
+            rcs_item = RevisionControlSystems.items[rcs_type.name]
+            try:
+                code_import = getUtility(ICodeImportSet).new(
+                    owner=branch_owner,
+                    registrant=self.user,
+                    context=self.context,
+                    branch_name=branch_name,
+                    rcs_type=rcs_item,
+                    target_rcs_type=TargetRevisionControlSystems.BZR,
+                    url=url,
+                    cvs_root=cvs_root,
+                    cvs_module=cvs_module)
+            except BranchExists as e:
+                self._setBranchExists(e.existing_branch, 'branch_name')
+                self.abort_update()
+                return
+            self.series.branch = code_import.branch
+            self.request.response.addInfoNotification(
+                'Code import created and branch linked to the series.')
+        else:
+            raise UnexpectedFormData(branch_type)
 
 
 class ProductRdfView(BaseRdfView):

=== modified file 'lib/lp/registry/browser/tests/productseries-setbranch-view.txt'
--- lib/lp/registry/browser/tests/productseries-setbranch-view.txt	2016-10-15 14:08:56 +0000
+++ lib/lp/registry/browser/tests/productseries-setbranch-view.txt	2016-10-15 14:08:56 +0000
@@ -39,7 +39,7 @@
 must be provided.
 
     >>> form = {
-    ...     'field.branch_type': 'link-lp-bzr',
+    ...     'field.branch_type': 'link-lp',
     ...     'field.actions.update': 'Update',
     ...     }
     >>> view = create_initialized_view(
@@ -52,7 +52,7 @@
 validation error.
 
     >>> form = {
-    ...     'field.branch_type': 'link-lp-bzr',
+    ...     'field.branch_type': 'link-lp',
     ...     'field.branch_location': 'foo',
     ...     'field.actions.update': 'Update',
     ...     }
@@ -69,7 +69,7 @@
     >>> branch = factory.makeBranch(
     ...     name='impala-branch', owner=driver, product=product)
     >>> form = {
-    ...     'field.branch_type': 'link-lp-bzr',
+    ...     'field.branch_type': 'link-lp',
     ...     'field.branch_location': branch.unique_name,
     ...     'field.actions.update': 'Update',
     ...     }

=== modified file 'lib/lp/registry/browser/tests/test_product.py'
--- lib/lp/registry/browser/tests/test_product.py	2016-09-19 11:47:33 +0000
+++ lib/lp/registry/browser/tests/test_product.py	2016-10-15 14:08:56 +0000
@@ -30,7 +30,10 @@
     InformationType,
     ServiceUsage,
     )
+from lp.code.enums import RevisionControlSystems
+from lp.code.interfaces.codeimport import CODE_IMPORT_GIT_TARGET_FEATURE_FLAG
 from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.code.tests.helpers import GitHostingFixture
 from lp.registry.browser.product import (
     ProjectAddStepOne,
     ProjectAddStepTwo,
@@ -47,6 +50,7 @@
 from lp.registry.model.product import Product
 from lp.services.config import config
 from lp.services.database.interfaces import IStore
+from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.publisher import canonical_url
 from lp.services.webapp.vhosts import allvhosts
 from lp.testing import (
@@ -914,7 +918,7 @@
         # control defaults to empty.
         project = self.factory.makeProduct()
         browser = self.getBrowser(project, '+configure-code')
-        self.assertEqual('', browser.getControl('Git repository').value)
+        self.assertEqual('', browser.getControl('Git repository:').value)
 
     def test_initial_git_repository(self):
         # If a project has a default Git repository, its "Git repository"
@@ -926,14 +930,16 @@
         unique_name = repo.unique_name
         browser = self.getBrowser(project, '+configure-code')
         self.assertEqual(
-            unique_name, browser.getControl('Git repository').value)
+            unique_name, browser.getControl('Git repository:').value)
 
     def test_link_existing_git_repository(self):
         repo = removeSecurityProxy(self.factory.makeGitRepository(
             target=self.factory.makeProduct()))
         browser = self.getBrowser(repo.project, '+configure-code')
         browser.getControl('Git', index=0).click()
-        browser.getControl('Git repository').value = repo.shortened_path
+        self.assertTrue(browser.getControl(
+            'Link to a Git repository already on Launchpad').selected)
+        browser.getControl('Git repository:').value = repo.shortened_path
         browser.getControl('Update').click()
 
         tag = Tag(
@@ -941,6 +947,62 @@
              text='Project settings updated.')
         self.assertThat(browser.contents, HTMLContains(tag))
 
+    def test_import_git_repository_requires_feature_flag(self):
+        project = self.factory.makeProduct()
+        browser = self.getBrowser(project, '+configure-code')
+        self.assertRaises(
+            LookupError, browser.getControl,
+            'Import a repository hosted somewhere else')
+
+    def test_import_git_repository(self):
+        self.useFixture(
+            FeatureFixture({CODE_IMPORT_GIT_TARGET_FEATURE_FLAG: u'on'}))
+        self.useFixture(GitHostingFixture())
+        owner = self.factory.makePerson()
+        project = self.factory.makeProduct(owner=owner)
+        browser = self.getBrowser(project, '+configure-code')
+        browser.getControl('Git', index=0).click()
+        browser.getControl('Import a repository hosted somewhere else').click()
+        browser.getControl('Repository name').value = 'imported'
+        browser.getControl('Repository URL').value = (
+            'https://git.example.org/imported')
+        browser.getControl('Update').click()
+
+        tag = Tag(
+            'success-div', 'div', attrs={'class': 'informational message'},
+             text='Code import created and repository set as default.')
+        self.assertThat(browser.contents, HTMLContains(tag))
+        login_person(owner)
+        repo = getUtility(IGitRepositorySet).getDefaultRepository(project)
+        self.assertIsNotNone(repo.code_import)
+        self.assertEqual(RevisionControlSystems.GIT, repo.code_import.rcs_type)
+        self.assertEqual(
+            'https://git.example.org/imported', repo.code_import.url)
+
+    def test_import_git_repository_bad_scheme(self):
+        self.useFixture(
+            FeatureFixture({CODE_IMPORT_GIT_TARGET_FEATURE_FLAG: u'on'}))
+        owner = self.factory.makePerson()
+        project = self.factory.makeProduct(owner=owner)
+        browser = self.getBrowser(project, '+configure-code')
+        browser.getControl('Git', index=0).click()
+        browser.getControl('Import a repository hosted somewhere else').click()
+        browser.getControl('Repository name').value = 'imported'
+        browser.getControl('Repository URL').value = (
+            'svn://svn.example.org/imported')
+        browser.getControl('Update').click()
+
+        tag = Tag(
+            'error', 'div', attrs={'class': 'message'},
+            text=(
+                'The URI scheme &quot;svn&quot; is not allowed.  '
+                'Only URIs with the following schemes may be used: '
+                'git, http, https'))
+        self.assertThat(browser.contents, HTMLContains(tag))
+        login_person(owner)
+        self.assertIsNone(
+            getUtility(IGitRepositorySet).getDefaultRepository(project))
+
     def test_editsshkeys_link_if_no_keys_registered(self):
         project = self.factory.makeProduct()
         browser = self.getBrowser(project, '+configure-code')

_______________________________________________
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to     : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp

Reply via email to