summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFinn Rayk Gaertner <finngaertner@protonmail.com>2026-07-31 15:41:44 +0000
committerUbuntu One Auto Copilot <otto-copilot@canonical.com>2026-07-31 15:41:44 +0000
commitb042f2deb3ecfaa583e2ab7763b0e6f8b706ff27 (patch)
tree516d5389f32b1c19ab8ed972784eb11502826f87
parent499e26eca6ad59864fc6a0d086e522b75687fd2d (diff)
parent7857904bbe254572f493e563c268343e9f0f18c6 (diff)
feat: Add new 'no_reuse_delta' flag to allow force repacks of specific reposHEADstablemaster
This allows us to use the new turnip API parameter introduced in https://code.launchpad.net/~finnrg/turnip/+git/turnip/+merge/509031 Merged from https://code.launchpad.net/~finnrg/launchpad/+git/launchpad/+merge/509033
-rw-r--r--lib/lp/code/interfaces/githosting.py5
-rw-r--r--lib/lp/code/interfaces/gitrepository.py9
-rw-r--r--lib/lp/code/model/githosting.py4
-rw-r--r--lib/lp/code/model/gitrepository.py6
-rw-r--r--lib/lp/code/model/tests/test_githosting.py11
-rw-r--r--lib/lp/code/model/tests/test_gitrepository.py21
6 files changed, 48 insertions, 8 deletions
diff --git a/lib/lp/code/interfaces/githosting.py b/lib/lp/code/interfaces/githosting.py
index 2f907bd309d..9f66b4e3bb3 100644
--- a/lib/lp/code/interfaces/githosting.py
+++ b/lib/lp/code/interfaces/githosting.py
@@ -168,12 +168,15 @@ class IGitHostingClient(Interface):
:param logger: An optional logger.
"""
- def repackRepository(path, logger=None):
+ def repackRepository(path, logger=None, no_reuse_delta=False):
"""Repack a Git repository.
:param path: Physical path of the new repository on the hosting
service.
:param logger: An optional logger.
+ :param no_reuse_delta: If True, request the hosting service to force a
+ full delta recomputation instead of reusing
+ existing deltas.
"""
def collectGarbage(path, logger=None):
diff --git a/lib/lp/code/interfaces/gitrepository.py b/lib/lp/code/interfaces/gitrepository.py
index 8862e044386..a0bc2e30f46 100644
--- a/lib/lp/code/interfaces/gitrepository.py
+++ b/lib/lp/code/interfaces/gitrepository.py
@@ -1075,8 +1075,15 @@ class IGitRepositoryExpensiveRequest(Interface):
"""
@export_write_operation()
+ @operation_parameters(
+ no_reuse_delta=Bool(
+ title=_("Force full delta recomputation"),
+ required=False,
+ default=False,
+ )
+ )
@operation_for_version("devel")
- def repackRepository():
+ def repackRepository(no_reuse_delta=False):
"""Trigger a repack repository operation.
Raises Unauthorized if the repack was attempted by a person
diff --git a/lib/lp/code/model/githosting.py b/lib/lp/code/model/githosting.py
index 1d4544cfe8d..e30ea3d04df 100644
--- a/lib/lp/code/model/githosting.py
+++ b/lib/lp/code/model/githosting.py
@@ -515,14 +515,14 @@ class GitHostingClient:
% (ref, path, e.response.status_code)
)
- def repackRepository(self, path, logger=None):
+ def repackRepository(self, path, logger=None, no_reuse_delta=False):
"""See `IGitHostingClient`."""
url = "/repo/%s/repack" % path
try:
if logger is not None:
logger.info("Repacking repository %s" % (path))
- return self._post(url)
+ return self._post(url, json={"no_reuse_delta": no_reuse_delta})
except requests.RequestException as e:
if (
e.response is not None
diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py
index 8b58b3d2d2a..525503035b6 100644
--- a/lib/lp/code/model/gitrepository.py
+++ b/lib/lp/code/model/gitrepository.py
@@ -503,8 +503,10 @@ class GitRepository(
namespace.moveRepository(self, user, rename_if_necessary=True)
self._reconcileAccess()
- def repackRepository(self):
- getUtility(IGitHostingClient).repackRepository(self.getInternalPath())
+ def repackRepository(self, no_reuse_delta=False):
+ getUtility(IGitHostingClient).repackRepository(
+ self.getInternalPath(), no_reuse_delta=no_reuse_delta
+ )
self.date_last_repacked = UTC_NOW
def collectGarbage(self):
diff --git a/lib/lp/code/model/tests/test_githosting.py b/lib/lp/code/model/tests/test_githosting.py
index 38dae61e112..b70a3809d27 100644
--- a/lib/lp/code/model/tests/test_githosting.py
+++ b/lib/lp/code/model/tests/test_githosting.py
@@ -719,6 +719,17 @@ class TestGitHostingClient(TestCase):
with self.mockRequests("POST", status=200):
repack = self.client.repackRepository("/repo/123")
self.assertEqual(None, repack)
+ [request] = self.requests
+ self.assertEqual({"no_reuse_delta": False}, json.loads(request.body))
+
+ def test_repack_no_reuse_delta(self):
+ with self.mockRequests("POST", status=200):
+ repack = self.client.repackRepository(
+ "/repo/123", no_reuse_delta=True
+ )
+ self.assertEqual(None, repack)
+ [request] = self.requests
+ self.assertEqual({"no_reuse_delta": True}, json.loads(request.body))
def test_repack_failure(self):
with self.mockRequests("POST", status=400):
diff --git a/lib/lp/code/model/tests/test_gitrepository.py b/lib/lp/code/model/tests/test_gitrepository.py
index 25a9004f138..4ddf12fc83b 100644
--- a/lib/lp/code/model/tests/test_gitrepository.py
+++ b/lib/lp/code/model/tests/test_gitrepository.py
@@ -5671,7 +5671,24 @@ class TestGitRepositoryWebservice(TestCaseWithFactory):
with person_logged_in(admin):
repository_db.repackRepository()
self.assertEqual(
- [((repository_db.getInternalPath(),), {})],
+ [((repository_db.getInternalPath(),), {"no_reuse_delta": False})],
+ hosting_fixture.repackRepository.calls,
+ )
+ self.assertEqual(1, hosting_fixture.repackRepository.call_count)
+
+ def test_repackRepository_no_reuse_delta(self):
+ # A repack can be requested with no_reuse_delta=True, which is
+ # forwarded to the hosting client.
+ hosting_fixture = self.useFixture(GitHostingFixture())
+ owner_db = self.factory.makePerson()
+ repository_db = self.factory.makeGitRepository(
+ owner=owner_db, name="repository"
+ )
+ admin = getUtility(ILaunchpadCelebrities).admin.teamowner
+ with person_logged_in(admin):
+ repository_db.repackRepository(no_reuse_delta=True)
+ self.assertEqual(
+ [((repository_db.getInternalPath(),), {"no_reuse_delta": True})],
hosting_fixture.repackRepository.calls,
)
self.assertEqual(1, hosting_fixture.repackRepository.call_count)
@@ -5692,7 +5709,7 @@ class TestGitRepositoryWebservice(TestCaseWithFactory):
with person_logged_in(person):
repository_db.repackRepository()
self.assertEqual(
- [((repository_db.getInternalPath(),), {})],
+ [((repository_db.getInternalPath(),), {"no_reuse_delta": False})],
hosting_fixture.repackRepository.calls,
)
self.assertEqual(1, hosting_fixture.repackRepository.call_count)