← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/git-target-inline-default-repo into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/git-target-inline-default-repo into lp:launchpad.

Commit message:
If a project has a default Git repository, show its branches on the project code page.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-target-inline-default-repo/+merge/258033

If a project has a default Git repository, show its branches on the project code page.

This is a rushed job.  We need to do something much smarter, perhaps having a mode switch to let a project say which it prefers, and the product-branch-summary handling is at best rough.  But it makes the repository at least be visible somehow, while it was previously hard to find.

I haven't done anything for packages yet; that will come a little later.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-target-inline-default-repo into lp:launchpad.
=== modified file 'lib/lp/code/browser/branchlisting.py'
--- lib/lp/code/browser/branchlisting.py	2015-04-22 12:03:05 +0000
+++ lib/lp/code/browser/branchlisting.py	2015-05-01 13:25:48 +0000
@@ -69,6 +69,7 @@
 from lp.bugs.interfaces.bugbranch import IBugBranchSet
 from lp.code.browser.branch import BranchMirrorMixin
 from lp.code.browser.branchmergeproposallisting import ActiveReviewsView
+from lp.code.browser.gitrepository import GitRefBatchNavigator
 from lp.code.browser.summary import BranchCountSummaryView
 from lp.code.enums import (
     BranchLifecycleStatus,
@@ -85,6 +86,7 @@
 from lp.code.interfaces.branchcollection import IAllBranches
 from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy
 from lp.code.interfaces.branchtarget import IBranchTarget
+from lp.code.interfaces.gitrepository import IGitRepositorySet
 from lp.code.interfaces.revision import IRevisionSet
 from lp.code.interfaces.revisioncache import IRevisionCache
 from lp.code.interfaces.seriessourcepackagebranch import (
@@ -525,6 +527,7 @@
     field_names = ['lifecycle', 'sort_by']
     development_focus_branch = None
     show_set_development_focus = False
+    default_git_repository = None
     custom_widget('lifecycle', LaunchpadDropdownWidget)
     custom_widget('sort_by', LaunchpadDropdownWidget)
     # Showing the series links is only really useful on product listing
@@ -1096,6 +1099,26 @@
         else:
             return None
 
+    @cachedproperty
+    def default_git_repository(self):
+        repository = getUtility(IGitRepositorySet).getDefaultRepository(
+            self.context)
+        if repository is None:
+            return None
+        elif check_permission('launchpad.View', repository):
+            return repository
+        else:
+            return None
+
+    def default_git_repository_branches(self):
+        """All branches in the default Git repository, sorted for display."""
+        return GitRefBatchNavigator(self, self.default_git_repository)
+
+    @property
+    def has_default_git_repository(self):
+        """Is there a default Git repository?"""
+        return self.default_git_repository is not None
+
     @property
     def no_branch_message(self):
         if (self.selected_lifecycle_status is not None

=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py	2015-04-21 09:31:58 +0000
+++ lib/lp/code/browser/gitrepository.py	2015-05-01 13:25:48 +0000
@@ -6,6 +6,7 @@
 __metaclass__ = type
 
 __all__ = [
+    'GitRefBatchNavigator',
     'GitRepositoryBreadcrumb',
     'GitRepositoryContextMenu',
     'GitRepositoryNavigation',
@@ -13,7 +14,7 @@
     'GitRepositoryView',
     ]
 
-from bzrlib import urlutils
+from storm.expr import Desc
 from zope.interface import implements
 
 from lp.app.browser.informationtype import InformationTypePortletMixin
@@ -115,13 +116,19 @@
     implements(IGitRefBatchNavigator)
 
     def __init__(self, view, context):
+        self.context = context
         super(GitRefBatchNavigator, self).__init__(
-            context.branches, view.request,
+            self._branches, view.request,
             size=config.launchpad.branchlisting_batch_size)
         self.view = view
         self.column_count = 3
 
     @property
+    def _branches(self):
+        from lp.code.model.gitref import GitRef
+        return self.context.branches.order_by(Desc(GitRef.committer_date))
+
+    @property
     def table_class(self):
         # XXX: MichaelHudson 2007-10-18 bug=153894: This means there are two
         # ways of sorting a one-page branch listing, which is confusing and
@@ -151,22 +158,6 @@
                 self.request, "launchpad.LimitedView", authorised_people)
 
     @property
-    def anon_url(self):
-        if self.context.visibleByUser(None):
-            return urlutils.join(
-                config.codehosting.git_anon_root, self.context.shortened_path)
-        else:
-            return None
-
-    @property
-    def ssh_url(self):
-        if self.user is not None:
-            return urlutils.join(
-                config.codehosting.git_ssh_root, self.context.shortened_path)
-        else:
-            return None
-
-    @property
     def user_can_push(self):
         """Whether the user can push to this branch."""
         return check_permission("launchpad.Edit", self.context)

=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py	2015-03-24 15:15:23 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py	2015-05-01 13:25:48 +0000
@@ -8,7 +8,6 @@
 from datetime import datetime
 
 from BeautifulSoup import BeautifulSoup
-from bzrlib import urlutils
 from fixtures import FakeLogger
 import pytz
 from testtools.matchers import Equals
@@ -21,7 +20,6 @@
 from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
 from lp.code.interfaces.revision import IRevisionSet
 from lp.registry.interfaces.person import PersonVisibility
-from lp.services.config import config
 from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.publisher import canonical_url
 from lp.testing import (
@@ -70,49 +68,6 @@
         super(TestGitRepositoryView, self).setUp()
         self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
 
-    def test_anon_url_for_public(self):
-        # Public repositories have an anonymous URL, visible to anyone.
-        repository = self.factory.makeGitRepository()
-        view = create_initialized_view(repository, "+index")
-        expected_url = urlutils.join(
-            config.codehosting.git_anon_root, repository.shortened_path)
-        self.assertEqual(expected_url, view.anon_url)
-
-    def test_anon_url_not_for_private(self):
-        # Private repositories do not have an anonymous URL.
-        owner = self.factory.makePerson()
-        repository = self.factory.makeGitRepository(
-            owner=owner, information_type=InformationType.USERDATA)
-        with person_logged_in(owner):
-            view = create_initialized_view(repository, "+index")
-            self.assertIsNone(view.anon_url)
-
-    def test_ssh_url_for_public_logged_in(self):
-        # Public repositories have an SSH URL, visible if logged in.
-        repository = self.factory.makeGitRepository()
-        with person_logged_in(repository.owner):
-            view = create_initialized_view(repository, "+index")
-            expected_url = urlutils.join(
-                config.codehosting.git_ssh_root, repository.shortened_path)
-            self.assertEqual(expected_url, view.ssh_url)
-
-    def test_ssh_url_for_public_not_anonymous(self):
-        # Public repositories do not have an SSH URL if not logged in.
-        repository = self.factory.makeGitRepository()
-        view = create_initialized_view(repository, "+index")
-        self.assertIsNone(view.ssh_url)
-
-    def test_ssh_url_for_private(self):
-        # Private repositories have an SSH URL.
-        owner = self.factory.makePerson()
-        repository = self.factory.makeGitRepository(
-            owner=owner, information_type=InformationType.USERDATA)
-        with person_logged_in(owner):
-            view = create_initialized_view(repository, "+index")
-            expected_url = urlutils.join(
-                config.codehosting.git_ssh_root, repository.shortened_path)
-            self.assertEqual(expected_url, view.ssh_url)
-
     def test_user_can_push(self):
         # A user can push if they have edit permissions.
         repository = self.factory.makeGitRepository()

=== modified file 'lib/lp/code/browser/tests/test_product.py'
--- lib/lp/code/browser/tests/test_product.py	2014-02-25 06:42:01 +0000
+++ lib/lp/code/browser/tests/test_product.py	2015-05-01 13:25:48 +0000
@@ -19,8 +19,13 @@
     ServiceUsage,
     )
 from lp.code.enums import BranchType
+from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
+    IGitRepositorySet,
+    )
 from lp.code.interfaces.revision import IRevisionSet
 from lp.registry.enums import BranchSharingPolicy
+from lp.services.features.testing import FeatureFixture
 from lp.services.webapp import canonical_url
 from lp.testing import (
     ANONYMOUS,
@@ -207,6 +212,39 @@
         expected = 'There are no branches for %s' % product.displayname
         self.assertIn(expected, html)
 
+    def test_no_default_git_repository(self):
+        # If there is no default Git repository, Product:+branches does not
+        # try to render one.
+        product = self.factory.makeProduct()
+        view = create_initialized_view(
+            product, '+branches', rootsite='code', principal=product.owner)
+        self.assertIsNone(view.default_git_repository)
+        self.assertFalse(view.has_default_git_repository)
+        content = view()
+        self.assertNotIn('git clone', content)
+
+    def test_default_git_repository(self):
+        # If there is a default Git repository, Product:+branches shows a
+        # summary of its branches.
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+        product = self.factory.makeProduct()
+        repository = self.factory.makeGitRepository(target=product)
+        self.factory.makeGitRefs(
+            repository=repository,
+            paths=[u"refs/heads/master", u"refs/heads/another-branch"])
+        with person_logged_in(product.owner):
+            getUtility(IGitRepositorySet).setDefaultRepository(
+                product, repository)
+        view = create_initialized_view(
+            product, '+branches', rootsite='code', principal=product.owner)
+        self.assertEqual(repository, view.default_git_repository)
+        self.assertTrue(view.has_default_git_repository)
+        content = view()
+        self.assertIn('git clone', content)
+        # XXX cjwatson 2015-04-30: These tests are not very precise.
+        self.assertIn('master', content)
+        self.assertIn('another-branch', content)
+
 
 class TestProductCodeIndexServiceUsages(ProductTestBase, BrowserTestCase):
     """Tests for the product code page, especially the usage messasges."""

=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py	2015-04-28 16:39:15 +0000
+++ lib/lp/code/interfaces/gitrepository.py	2015-05-01 13:25:48 +0000
@@ -217,6 +217,12 @@
         "The identity of this repository: a VCS-independent synonym for "
         "git_identity.")
 
+    anon_url = Attribute(
+        "An anonymous (git://) URL for this repository, or None in the case "
+        "of private repositories.")
+
+    ssh_url = Attribute("A git+ssh:// URL for this repository.")
+
     refs = exported(CollectionField(
         title=_("The references present in this repository."),
         readonly=True,

=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py	2015-04-28 16:39:15 +0000
+++ lib/lp/code/model/gitrepository.py	2015-05-01 13:25:48 +0000
@@ -322,6 +322,21 @@
             config.codehosting.git_browse_root, self.unique_name)
 
     @property
+    def anon_url(self):
+        """See `IGitRepository`."""
+        if self.visibleByUser(None):
+            return urlutils.join(
+                config.codehosting.git_anon_root, self.shortened_path)
+        else:
+            return None
+
+    @property
+    def ssh_url(self):
+        """See `IGitRepository`."""
+        return urlutils.join(
+            config.codehosting.git_ssh_root, self.shortened_path)
+
+    @property
     def private(self):
         return self.information_type in PRIVATE_INFORMATION_TYPES
 

=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py	2015-04-28 16:39:15 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py	2015-05-01 13:25:48 +0000
@@ -11,6 +11,7 @@
 import hashlib
 import json
 
+from bzrlib import urlutils
 from lazr.lifecycle.event import ObjectModifiedEvent
 from lazr.lifecycle.snapshot import Snapshot
 import transaction
@@ -72,6 +73,7 @@
     )
 from lp.registry.interfaces.personproduct import IPersonProductFactory
 from lp.registry.tests.test_accesspolicy import get_policies_for_artifact
+from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
 from lp.services.features.testing import FeatureFixture
 from lp.services.mail import stub
@@ -353,21 +355,53 @@
     # actually notices any interesting kind of repository modifications.
 
 
-class TestCodebrowse(TestCaseWithFactory):
-    """Tests for Git repository codebrowse support."""
+class TestGitRepositoryURLs(TestCaseWithFactory):
+    """Tests for Git repository URLs."""
 
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCodebrowse, self).setUp()
+        super(TestGitRepositoryURLs, self).setUp()
         self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
 
-    def test_simple(self):
+    def test_codebrowse_url(self):
         # The basic codebrowse URL for a repository is an 'https' URL.
         repository = self.factory.makeGitRepository()
-        self.assertEqual(
-            "https://git.launchpad.dev/"; + repository.unique_name,
-            repository.getCodebrowseUrl())
+        expected_url = urlutils.join(
+            config.codehosting.git_browse_root, repository.unique_name)
+        self.assertEqual(expected_url, repository.getCodebrowseUrl())
+
+    def test_anon_url_for_public(self):
+        # Public repositories have an anonymous URL, visible to anyone.
+        repository = self.factory.makeGitRepository()
+        expected_url = urlutils.join(
+            config.codehosting.git_anon_root, repository.shortened_path)
+        self.assertEqual(expected_url, repository.anon_url)
+
+    def test_anon_url_not_for_private(self):
+        # Private repositories do not have an anonymous URL.
+        owner = self.factory.makePerson()
+        repository = self.factory.makeGitRepository(
+            owner=owner, information_type=InformationType.USERDATA)
+        with person_logged_in(owner):
+            self.assertIsNone(repository.anon_url)
+
+    def test_ssh_url_for_public(self):
+        # Public repositories have an SSH URL.
+        repository = self.factory.makeGitRepository()
+        expected_url = urlutils.join(
+            config.codehosting.git_ssh_root, repository.shortened_path)
+        self.assertEqual(expected_url, repository.ssh_url)
+
+    def test_ssh_url_for_private(self):
+        # Private repositories have an SSH URL.
+        owner = self.factory.makePerson()
+        repository = self.factory.makeGitRepository(
+            owner=owner, information_type=InformationType.USERDATA)
+        with person_logged_in(owner):
+            expected_url = urlutils.join(
+                config.codehosting.git_ssh_root, repository.shortened_path)
+            self.assertEqual(expected_url, repository.ssh_url)
 
 
 class TestGitRepositoryNamespace(TestCaseWithFactory):

=== modified file 'lib/lp/code/templates/gitref-listing.pt'
--- lib/lp/code/templates/gitref-listing.pt	2015-04-29 15:06:39 +0000
+++ lib/lp/code/templates/gitref-listing.pt	2015-05-01 13:25:48 +0000
@@ -24,7 +24,7 @@
     </div>
   </tal:needs-batch>
 
-  <table tal:attributes="class context/table_class" id="branchtable">
+  <table tal:attributes="class context/table_class" id="gitreftable">
     <thead>
       <tr>
         <th>Name</th>

=== modified file 'lib/lp/code/templates/gitrepository-management.pt'
--- lib/lp/code/templates/gitrepository-management.pt	2015-03-04 16:49:42 +0000
+++ lib/lp/code/templates/gitrepository-management.pt	2015-05-01 13:25:48 +0000
@@ -7,15 +7,15 @@
   <dl id="clone-url">
     <dt>Get this repository:</dt>
     <dd>
-      <tal:anonymous condition="view/anon_url">
+      <tal:anonymous condition="context/anon_url">
         <tt class="command">
-          git clone <span class="anon-url" tal:content="view/anon_url" />
+          git clone <span class="anon-url" tal:content="context/anon_url" />
         </tt>
         <br />
       </tal:anonymous>
-      <tal:ssh condition="view/ssh_url">
+      <tal:ssh condition="view/user">
         <tt class="command">
-          git clone <span class="ssh-url" tal:content="view/ssh_url" />
+          git clone <span class="ssh-url" tal:content="context/ssh_url" />
         </tt>
       </tal:ssh>
     </dd>

=== modified file 'lib/lp/code/templates/product-branch-summary.pt'
--- lib/lp/code/templates/product-branch-summary.pt	2012-10-08 02:02:19 +0000
+++ lib/lp/code/templates/product-branch-summary.pt	2015-05-01 13:25:48 +0000
@@ -56,7 +56,8 @@
     </p>
   </div>
 
-  <tal:no-branches condition="not: view/branch_count">
+  <tal:no-branches
+      condition="python: not view.branch_count and not view.has_default_git_repository">
     There are no branches for <tal:project-name replace="context/displayname"/>
     in Launchpad.
     <tal:can-configure condition="view/can_configure_branches">
@@ -78,6 +79,23 @@
     </tal:can-configure>
   </tal:no-branches>
 
+  <div tal:condition="view/has_default_git_repository"
+       style="margin: 1em 0"
+       tal:define="repository view/default_git_repository">
+    You can
+    <a tal:attributes="href repository/getCodebrowseUrl">browse the
+      source code</a>
+    for the default Git repository or get a copy of the repository using
+    the command:<br/>
+    <tt class="command">git clone
+    <tal:logged-in condition="view/user">
+      <tal:git-ssh-url replace="repository/ssh_url"/>
+    </tal:logged-in>
+    <tal:not-logged-in condition="not: view/user">
+      <tal:git-anon-url replace="repository/anon_url"/>
+    </tal:not-logged-in>
+  </div>
+
   <tal:has-branches condition="view/branch_count">
     <div tal:condition="view/has_development_focus_branch"
          style="margin: 1em 0"
@@ -99,7 +117,7 @@
   <tal:has-user condition="view/user">
     <p id="push-instructions"
       tal:condition="not: context/codehosting_usage/enumvalue:UNKNOWN">
-      You can push the branch directly to Launchpad with the command:<br />
+      You can push a Bazaar branch directly to Launchpad with the command:<br />
       <tt class="command">
         bzr push lp:~<tal:user replace="view/user/name"/>/<tal:project replace="context/name"/>/<tal:series replace="context/name"/>
       </tt>

=== modified file 'lib/lp/code/templates/product-branches.pt'
--- lib/lp/code/templates/product-branches.pt	2012-10-08 02:02:19 +0000
+++ lib/lp/code/templates/product-branches.pt	2015-05-01 13:25:48 +0000
@@ -60,9 +60,22 @@
       condition="not: view/context/codehosting_usage/enumvalue:UNKNOWN"
       replace="structure context/@@+portlet-product-codestatistics" />
 
+    <tal:has-default-git-repository condition="view/has_default_git_repository">
+      <div id="default-repository-branches" class="portlet"
+           tal:define="repository view/default_git_repository;
+                       branches view/default_git_repository_branches">
+        <h2>Git branches</h2>
+        <tal:default-repository-branches
+          replace="structure branches/@@+ref-listing" />
+      </div>
+    </tal:has-default-git-repository>
+
     <tal:has-branches condition="view/branch_count"
                       define="branches view/branches">
-      <tal:branchlisting content="structure branches/@@+branch-listing" />
+      <div class="portlet">
+        <h2>Bazaar branches</h2>
+        <tal:branchlisting content="structure branches/@@+branch-listing" />
+      </div>
     </tal:has-branches>
 
   </tal:main>

=== modified file 'lib/lp/registry/doc/product.txt'
--- lib/lp/registry/doc/product.txt	2015-01-29 18:43:52 +0000
+++ lib/lp/registry/doc/product.txt	2015-05-01 13:25:48 +0000
@@ -521,6 +521,31 @@
     landscape
 
 
+Products with Git repositories
+------------------------------
+
+Products are considered to officially support Launchpad as a location for
+their code if they have a default Git repository.
+
+    >>> from lp.code.interfaces.gitrepository import (
+    ...     GIT_FEATURE_FLAG,
+    ...     IGitRepositorySet,
+    ...     )
+    >>> from lp.services.features.testing import FeatureFixture
+    >>> firefox.development_focus.branch = None
+    >>> print firefox.official_codehosting
+    False
+    >>> print firefox.codehosting_usage.name
+    UNKNOWN
+    >>> with FeatureFixture({GIT_FEATURE_FLAG: 'on'}):
+    ...     getUtility(IGitRepositorySet).setDefaultRepository(
+    ...         firefox, factory.makeGitRepository(target=firefox))
+    >>> print firefox.official_codehosting
+    True
+    >>> print firefox.codehosting_usage.name
+    LAUNCHPAD
+
+
 Primary translatable
 --------------------
 

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2015-03-17 10:45:07 +0000
+++ lib/lp/registry/model/product.py	2015-05-01 13:25:48 +0000
@@ -116,6 +116,7 @@
     )
 from lp.code.enums import BranchType
 from lp.code.interfaces.branch import DEFAULT_BRANCH_STATUS_IN_LISTING
+from lp.code.interfaces.gitrepository import IGitRepositorySet
 from lp.code.model.branch import Branch
 from lp.code.model.branchnamespace import BRANCH_POLICY_ALLOWED_TYPES
 from lp.code.model.hasbranches import (
@@ -581,7 +582,10 @@
 
     @property
     def official_codehosting(self):
-        return self.development_focus.branch is not None
+        repository = getUtility(IGitRepositorySet).getDefaultRepository(self)
+        return (
+            self.development_focus.branch is not None or
+            repository is not None)
 
     @property
     def official_anything(self):
@@ -613,9 +617,11 @@
 
     @property
     def codehosting_usage(self):
-        if self.development_focus.branch is None:
+        repository = getUtility(IGitRepositorySet).getDefaultRepository(self)
+        if self.development_focus.branch is None and repository is None:
             return ServiceUsage.UNKNOWN
-        elif self.development_focus.branch.branch_type == BranchType.HOSTED:
+        elif (repository is not None or
+              self.development_focus.branch.branch_type == BranchType.HOSTED):
             return ServiceUsage.LAUNCHPAD
         elif self.development_focus.branch.branch_type in (
             BranchType.MIRRORED,

=== modified file 'lib/lp/registry/templates/productseries-setbranch.pt'
--- lib/lp/registry/templates/productseries-setbranch.pt	2012-10-09 01:07:52 +0000
+++ lib/lp/registry/templates/productseries-setbranch.pt	2015-05-01 13:25:48 +0000
@@ -19,7 +19,7 @@
 <div metal:fill-slot="main">
 
   <p id="push-instructions">
-    You can push the branch directly to Launchpad with the command:<br />
+    You can push a Bazaar branch directly to Launchpad with the command:<br />
     <tt class="command">
       bzr push lp:~<tal:user replace="view/user/name"/>/<tal:project replace="context/product/name"/>/<tal:series replace="context/name"/>
     </tt>

=== modified file 'lib/lp/registry/tests/test_service_usage.py'
--- lib/lp/registry/tests/test_service_usage.py	2012-01-01 02:58:52 +0000
+++ lib/lp/registry/tests/test_service_usage.py	2015-05-01 13:25:48 +0000
@@ -3,8 +3,15 @@
 
 __metaclass__ = type
 
+from zope.component import getUtility
+
 from lp.app.enums import ServiceUsage
 from lp.code.enums import BranchType
+from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
+    IGitRepositorySet,
+    )
+from lp.services.features.testing import FeatureFixture
 from lp.testing import (
     login_person,
     TestCaseWithFactory,
@@ -192,7 +199,7 @@
             self.target.codehosting_usage)
 
     def test_codehosting_hosted_branch(self):
-        # A branch on Launchpad is HOSTED.
+        # A branch on Launchpad has LAUNCHPAD usage.
         login_person(self.target.owner)
         self.target.development_focus.branch = self.factory.makeProductBranch(
             product=self.target,
@@ -201,6 +208,17 @@
             ServiceUsage.LAUNCHPAD,
             self.target.codehosting_usage)
 
+    def test_codehosting_default_git_repository(self):
+        # A default Git repository on Launchpad has LAUNCHPAD usage.
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+        login_person(self.target.owner)
+        repository = self.factory.makeGitRepository(target=self.target)
+        getUtility(IGitRepositorySet).setDefaultRepository(
+            self.target, repository)
+        self.assertEqual(
+            ServiceUsage.LAUNCHPAD,
+            self.target.codehosting_usage)
+
 
 class TestProductSeriesUsageEnums(
     TestCaseWithFactory,


Follow ups