launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25255
[Merge] ~pappacena/turnip:copy-ref-helper into turnip:master
Thiago F. Pappacena has proposed merging ~pappacena/turnip:copy-ref-helper into turnip:master with ~pappacena/turnip:celery-test-fixture as a prerequisite.
Commit message:
Celery task to copy refs between repositories and delete them
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/turnip/+git/turnip/+merge/390205
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/turnip:copy-ref-helper into turnip:master.
diff --git a/turnip/api/store.py b/turnip/api/store.py
index f4df7e9..0a1ea72 100644
--- a/turnip/api/store.py
+++ b/turnip/api/store.py
@@ -28,7 +28,7 @@ from pygit2 import (
)
from turnip.pack.helpers import ensure_config
-
+from turnip.tasks import app
REF_TYPE_NAME = {
GIT_OBJ_COMMIT: 'commit',
@@ -114,6 +114,41 @@ def write_alternates(repo_path, alternate_repo_paths):
object_dir_re = re.compile(r'\A[0-9a-f][0-9a-f]\Z')
+@app.task
+def copy_ref(from_root, from_ref, to_root, to_ref=None):
+ """Copy a single ref from one git repository to another.
+
+ If `to_ref` is None, the copy will use the `from_ref` name from
+ origin to the destination repository.
+
+ This is implemented now using git client's "git fetch" command,
+ since it's way easier than trying to copy the refs, commits and objects
+ manually using pygit.
+
+ :param from_root: The root directory of the source git repository.
+ :param from_ref: The source ref name.
+ :param to_root: The root directory of the destination git repository.
+ :param to_ref: The destination ref name. If None, copy_ref will use the
+ source ref name.
+ """
+ if to_ref is None:
+ to_ref = from_ref
+ cmd = [
+ b'git', b'fetch', b'--no-tags',
+ from_root, b'%s:%s' % (from_ref, to_ref)
+ ]
+ proc = subprocess.Popen(
+ cmd, cwd=to_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if proc.wait() != 0:
+ raise GitError("Error copying refs: %s" % proc.stderr.read())
+
+
+@app.task
+def delete_ref(repo_root, ref_name):
+ repo = Repository(repo_root)
+ repo.references[ref_name].delete()
+
+
def copy_refs(from_root, to_root):
"""Copy refs from one .git directory to another.
diff --git a/turnip/api/tests/test_store.py b/turnip/api/tests/test_store.py
index 02ed3fd..3d8f3ce 100644
--- a/turnip/api/tests/test_store.py
+++ b/turnip/api/tests/test_store.py
@@ -26,6 +26,7 @@ from turnip.api.tests.test_helpers import (
open_repo,
RepoFactory,
)
+from turnip.tests.tasks import CeleryWorkerFixture
class InitTestCase(TestCase):
@@ -95,8 +96,9 @@ class InitTestCase(TestCase):
def makeOrig(self):
self.orig_path = os.path.join(self.repo_store, 'orig/')
- orig = RepoFactory(
- self.orig_path, num_branches=3, num_commits=2, num_tags=2).build()
+ self.orig_factory = RepoFactory(
+ self.orig_path, num_branches=3, num_commits=2, num_tags=2)
+ orig = self.orig_factory.build()
self.orig_refs = {}
for ref in orig.references.objects:
obj = orig[ref.target]
@@ -356,3 +358,89 @@ class InitTestCase(TestCase):
self.orig_refs, os.path.join(to_path, 'turnip-subordinate'))
self.assertPackedRefs(
packed_refs, os.path.join(too_path, 'turnip-subordinate'))
+
+ def test_copy_ref(self):
+ celery_fixture = CeleryWorkerFixture()
+ self.useFixture(celery_fixture)
+
+ self.makeOrig()
+ # Creates a new branch in the orig repository.
+ orig_path = self.orig_path
+ orig = self.orig_factory.repo
+ master_tip = orig.references[b'refs/heads/master'].target.hex
+
+ orig_branch_name = b'new-branch'
+ orig_ref_name = b'refs/heads/new-branch'
+ orig.create_branch(orig_branch_name, orig[master_tip])
+ orig_commit_oid = self.orig_factory.add_commit(
+ b'foobar file content', 'foobar.txt', parents=[master_tip],
+ ref=orig_ref_name)
+ orig_blob_id = orig[orig_commit_oid].tree[0].id
+
+ dest_path = os.path.join(self.repo_store, 'to/')
+ store.init_repo(dest_path, clone_from=self.orig_path)
+
+ dest = pygit2.Repository(dest_path)
+ self.assertEqual([], dest.references.objects)
+
+ dest_ref_name = b'refs/merge/123'
+ store.copy_ref.apply_async(
+ (orig_path, orig_ref_name, dest_path, dest_ref_name))
+ celery_fixture.waitUntil(5, lambda: len(dest.references.objects) == 1)
+
+ self.assertEqual(1, len(dest.references.objects))
+ copied_ref = dest.references.objects[0]
+ self.assertEqual(dest_ref_name, copied_ref.name)
+ self.assertEqual(
+ orig.references[orig_ref_name].target,
+ dest.references[dest_ref_name].target)
+ self.assertEqual(b'foobar file content', dest[orig_blob_id].data)
+
+ # Updating and copying again should work.
+ orig_commit_oid = self.orig_factory.add_commit(
+ b'changed foobar content', 'foobar.txt', parents=[orig_commit_oid],
+ ref=orig_ref_name)
+ orig_blob_id = orig[orig_commit_oid].tree[0].id
+
+ store.copy_ref.apply_async(
+ (orig_path, orig_ref_name, dest_path, dest_ref_name))
+
+ def waitForNewCommit():
+ try:
+ return dest[orig_blob_id].data == b'changed foobar content'
+ except KeyError:
+ return False
+ celery_fixture.waitUntil(5, waitForNewCommit)
+
+ self.assertEqual(1, len(dest.references.objects))
+ copied_ref = dest.references.objects[0]
+ self.assertEqual(dest_ref_name, copied_ref.name)
+ self.assertEqual(
+ orig.references[orig_ref_name].target,
+ dest.references[dest_ref_name].target)
+ self.assertEqual(b'changed foobar content', dest[orig_blob_id].data)
+
+ def test_delete_ref(self):
+ celery_fixture = CeleryWorkerFixture()
+ self.useFixture(celery_fixture)
+
+ self.makeOrig()
+ orig_path = self.orig_path
+ orig = self.orig_factory.repo
+
+ master_tip = orig.references[b'refs/heads/master'].target.hex
+ new_branch_name = b'new-branch'
+ new_ref_name = b'refs/heads/new-branch'
+ orig.create_branch(new_branch_name, orig[master_tip])
+ self.orig_factory.add_commit(
+ b'foobar file content', 'foobar.txt', parents=[master_tip],
+ ref=new_ref_name)
+
+ before_refs_len = len(orig.references.objects)
+ store.delete_ref.apply_async((orig_path, new_ref_name))
+ celery_fixture.waitUntil(
+ 5, lambda: len(orig.references.objects) < before_refs_len)
+
+ self.assertEqual(before_refs_len - 1, len(orig.references.objects))
+ self.assertNotIn(
+ new_branch_name, [i.name for i in orig.references.objects])