← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~blr/turnip/api-init-with-alternates into lp:turnip

 

Bayard 'kit' Randel has proposed merging lp:~blr/turnip/api-init-with-alternates into lp:turnip.

Commit message:
Add support for repo init with alternates.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~blr/turnip/api-init-with-alternates/+merge/256715

A list of repo_alternate_paths can be posted to /repo/{name} to set object alternates. 
This MP also adds initial unit tests for the api store.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~blr/turnip/api-init-with-alternates into lp:turnip.
=== modified file 'turnip/api/store.py'
--- turnip/api/store.py	2015-04-16 21:47:08 +0000
+++ turnip/api/store.py	2015-04-17 21:06:50 +0000
@@ -63,6 +63,10 @@
         }
 
 
+def is_bare(repo_path):
+    return not os.path.exists(os.path.join(repo_path, '.git'))
+
+
 def is_valid_new_path(path):
     """Verify repo path is new, or raise Exception."""
     if os.path.exists(path):
@@ -70,7 +74,25 @@
     return True
 
 
-def init_repo(repo_path, clone_from=None, is_bare=True):
+def alternates_path(repo_path):
+    """Git object alternates path.
+    See http://git-scm.com/docs/gitrepository-layout
+    """
+    return os.path.join(repo_path, 'objects', 'info', 'alternates')
+
+
+def write_alternates(repo_path, alternate_repo_paths):
+    with open(alternates_path(repo_path), "w") as f:
+        for path in alternate_repo_paths:
+            if is_bare(path):
+                objects_path = os.path.join(path, 'objects')
+            else:
+                objects_path = os.path.join(path, '.git', 'objects')
+            f.write("{}\n".format(objects_path))
+
+
+def init_repo(repo_path, clone_from=None, alternate_repo_paths=None,
+              is_bare=True):
     """Initialise a new git repository or clone from existing."""
     assert is_valid_new_path(repo_path)
     if clone_from:
@@ -79,6 +101,8 @@
         repo = clone_repository(clone_from_url, repo_path, is_bare)
     else:
         repo = init_repository(repo_path, is_bare)
+    if alternate_repo_paths:
+        write_alternates(repo_path, alternate_repo_paths)
     return repo.path
 
 

=== modified file 'turnip/api/tests/test_api.py'
--- turnip/api/tests/test_api.py	2015-04-16 21:47:08 +0000
+++ turnip/api/tests/test_api.py	2015-04-17 21:06:50 +0000
@@ -72,6 +72,19 @@
         self.assertEqual(200, resp.status_code)
         self.assertIn(new_repo_path, resp.json['repo_url'])
 
+    def test_repo_init_with_alternate(self):
+        """Repo can be initialised with alternate."""
+        factory = RepoFactory(self.repo_store)
+        commit_oid = factory.add_commit('foo', 'foobar.txt')
+        new_repo_path = uuid.uuid4().hex
+        json = {'repo_path': new_repo_path,
+                'alternate_repo_paths': [self.repo_store]}
+        resp = self.app.post_json('/repo', json)
+        self.assertEqual(200, resp.status_code)
+        resp_commit = self.app.get('/repo/{}/commits/{}'.format(
+            new_repo_path, commit_oid))
+        self.assertEqual(200, resp_commit.status_code)
+
     def test_repo_delete(self):
         self.app.post_json('/repo', {'repo_path': self.repo_path})
         resp = self.app.delete('/repo/{}'.format(self.repo_path))

=== added file 'turnip/api/tests/test_store.py'
--- turnip/api/tests/test_store.py	1970-01-01 00:00:00 +0000
+++ turnip/api/tests/test_store.py	2015-04-17 21:06:50 +0000
@@ -0,0 +1,57 @@
+# Copyright 2015 Canonical Ltd.  All rights reserved.
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+
+import os
+import unittest
+import uuid
+
+from fixtures import (
+    EnvironmentVariable,
+    TempDir,
+    )
+from testtools import TestCase
+
+from turnip.api.store import (
+    alternates_path,
+    init_repo
+    )
+from turnip.api.tests.test_helpers import (
+    open_repo,
+    RepoFactory,
+    )
+
+
+class StoreTestCase(TestCase):
+
+    def setUp(self):
+        super(StoreTestCase, self).setUp()
+        self.repo_store = self.useFixture(TempDir()).path
+        self.useFixture(EnvironmentVariable("REPO_STORE", self.repo_store))
+        self.repo_path = os.path.join(self.repo_store, uuid.uuid1().hex)
+
+    def test_repo_with_alternates(self):
+        """Ensure objects path is defined correctly in repo alternates."""
+        factory = RepoFactory(self.repo_path)
+        new_repo_path = os.path.join(self.repo_store, uuid.uuid1().hex)
+        repo_path_with_alt = init_repo(
+            new_repo_path, alternate_repo_paths=[factory.repo.path])
+        objects_path = '{}\n'.format(
+            os.path.join(factory.repo.path, 'objects'))
+        with open(alternates_path(repo_path_with_alt), 'r') as alts:
+            alts_content = alts.read()
+            self.assertEquals(objects_path, alts_content)
+
+    def test_repo_alternates_objects_shared(self):
+        """Ensure objects are shared from alternate repo."""
+        factory = RepoFactory(self.repo_path)
+        commit_oid = factory.add_commit('foo', 'foobar.txt')
+        new_repo_path = os.path.join(self.repo_store, uuid.uuid4().hex)
+        repo_path_with_alt = init_repo(
+            new_repo_path, alternate_repo_paths=[factory.repo.path])
+        repo_with_alt = open_repo(repo_path_with_alt)
+        self.assertEqual(commit_oid.hex, repo_with_alt.get(commit_oid).hex)
+
+
+if __name__ == '__main__':
+    unittest.main()

=== modified file 'turnip/api/views.py'
--- turnip/api/views.py	2015-04-16 21:47:08 +0000
+++ turnip/api/views.py	2015-04-17 21:06:50 +0000
@@ -55,6 +55,8 @@
         """Initialise a new git repository, or clone from an existing repo."""
         repo_path = extract_json_data(self.request).get('repo_path')
         clone_path = extract_json_data(self.request).get('clone_from')
+        alternate_repo_paths = extract_json_data(self.request).get(
+            'alternate_repo_paths')
 
         if not repo_path:
             self.request.errors.add('body', 'repo_path',
@@ -71,7 +73,8 @@
             repo_clone = None
 
         try:
-            new_repo_path = store.init_repo(repo, repo_clone)
+            new_repo_path = store.init_repo(
+                repo, repo_clone, alternate_repo_paths=alternate_repo_paths)
             repo_name = os.path.basename(os.path.normpath(new_repo_path))
             return {'repo_url': '/'.join([self.request.url, repo_name])}
         except GitError:


Follow ups