launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18031
[Merge] lp:~blr/turnip/api-diff into lp:turnip
Bayard 'kit' Randel has proposed merging lp:~blr/turnip/api-diff into lp:turnip with lp:~blr/turnip/api-refs as a prerequisite.
Requested reviews:
William Grant (wgrant)
For more details, see:
https://code.launchpad.net/~blr/turnip/api-diff/+merge/251855
Initial diff api, with additional test helpers for index staging and creating commits from arbitrary blob data and paths.
--
Your team Launchpad code reviewers is subscribed to branch lp:turnip.
=== modified file 'turnip/api/store.py'
--- turnip/api/store.py 2015-03-05 01:24:43 +0000
+++ turnip/api/store.py 2015-03-05 01:24:43 +0000
@@ -79,3 +79,14 @@
"object": {'sha': git_object.oid.hex,
'type': get_ref_type_name(git_object.type)}}
return ref
+
+ @staticmethod
+ def get_diff(repo_path, hash1, hash2):
+ """Get diff of two commits."""
+ repo = Store.open_repo(repo_path)
+ shas = [hash1, hash2]
+ try:
+ commits = [repo.revparse_single(sha) for sha in shas]
+ except (TypeError, KeyError):
+ raise
+ return repo.diff(commits[0], commits[1]).patch
=== modified file 'turnip/api/tests/test_api.py'
--- turnip/api/tests/test_api.py 2015-03-05 01:24:43 +0000
+++ turnip/api/tests/test_api.py 2015-03-05 01:24:43 +0000
@@ -15,7 +15,7 @@
from webtest import TestApp
from turnip import api
-from turnip.api.tests import test_helpers
+from turnip.api.tests.test_helpers import RepoFactory
class ApiTestCase(TestCase):
@@ -28,11 +28,9 @@
self.repo_path = str(uuid.uuid1())
self.repo_store = os.path.join(repo_store, self.repo_path)
self.commit = {'ref': 'refs/heads/master', 'message': 'test commit.'}
- self.tag = {'name': 'test-tag', 'message': 'tag message'}
+ self.tag = {'ref': 'refs/tags/tag0', 'message': 'tag message'}
def get_ref(self, ref):
- test_helpers.init_repo(self.repo_store,
- commits=[self.commit], tags=[self.tag])
resp = self.app.get('/repo/{}/{}'.format(
self.repo_path, ref))
return json.loads(resp.json_body)
@@ -51,28 +49,42 @@
def test_repo_get_refs(self):
"""Ensure expected ref objects are returned and shas match."""
ref = self.commit.get('ref')
- repo = test_helpers.init_repo(self.repo_store,
- commits=[self.commit], tags=[self.tag])
+ repo = RepoFactory(self.repo_store, num_commits=1, num_tags=1).build()
resp = self.app.get('/repo/{}/refs'.format(self.repo_path))
body = json.loads(resp.json_body)
self.assertTrue(ref in body)
- self.assertTrue('refs/tags/{}'.format(self.tag.get('name') in body))
+ self.assertTrue(self.tag.get('ref') in body)
oid = repo.head.get_object().oid.hex # git object sha
resp_sha = body[ref]['object'].get('sha')
self.assertEqual(oid, resp_sha)
def test_repo_get_ref(self):
- ref = 'refs/heads/master'
+ RepoFactory(self.repo_store, num_commits=1).build()
+ ref = self.commit.get('ref')
resp = self.get_ref(ref)
self.assertEqual(ref, resp['ref'])
def test_repo_get_tag(self):
- tag = 'refs/tags/test-tag'
+ RepoFactory(self.repo_store, num_commits=1, num_tags=1).build()
+ tag = self.tag.get('ref')
resp = self.get_ref(tag)
self.assertEqual(tag, resp['ref'])
+ def test_repo_compare_commits(self):
+ repo = RepoFactory(self.repo_store)
+
+ c1_oid = repo.add_commit('foo', 'foobar.txt')
+ c2_oid = repo.add_commit('bar', 'foobar.txt', parents=[c1_oid])
+
+ path = '/repo/{}/compare/{}..{}'.format(self.repo_path, c1_oid, c2_oid)
+ resp = self.app.get(path)
+ resp_body = json.loads(resp.body)
+ self.assertTrue('-foo' in resp_body)
+ self.assertTrue('+bar' in resp_body)
+
if __name__ == '__main__':
+
unittest.main()
=== modified file 'turnip/api/tests/test_helpers.py'
--- turnip/api/tests/test_helpers.py 2015-03-05 01:24:43 +0000
+++ turnip/api/tests/test_helpers.py 2015-03-05 01:24:43 +0000
@@ -1,39 +1,85 @@
# Copyright 2015 Canonical Ltd. All rights reserved.
+import os
+
from pygit2 import (
init_repository,
+ GIT_FILEMODE_BLOB,
GIT_OBJ_COMMIT,
+ GIT_SORT_TIME,
+ IndexEntry,
Signature,
)
-AUTHOR = Signature('Test Author', 'author@xxxxxxx')
-COMMITTER = Signature('Test Commiter', 'committer@xxxxxxx')
-
-
-def create_commits(repo, commits, parents=[]):
- tree = repo.TreeBuilder().write()
- for commit in commits:
- commit = repo.create_commit(
- commit['ref'],
- AUTHOR, COMMITTER, commit['message'],
- tree,
- parents
- )
- return repo
-
-
-def create_tags(repo, tags):
- oid = repo.head.get_object().oid
- for tag in tags:
- tag = repo.create_tag(
- tag['name'], oid, GIT_OBJ_COMMIT, COMMITTER, tag['message'])
- return repo
-
-
-def init_repo(repo_path, commits=None, tags=None):
- repo = init_repository(repo_path, True)
- if commits:
- repo = create_commits(repo, commits)
- if tags:
- repo = create_tags(repo, tags)
- return repo
+
+class RepoFactory():
+ """Builds a git repository in a user defined state."""
+
+ def __init__(self, repo_store=None, num_commits=None, num_tags=None):
+ self.author = Signature('Test Author', 'author@xxxxxxx')
+ self.committer = Signature('Test Commiter', 'committer@xxxxxxx')
+ self.num_commits = num_commits
+ self.num_tags = num_tags
+ self.repo_store = repo_store
+ self.repo = self.init_repo()
+
+ @property
+ def commits(self):
+ """Walk repo from HEAD and returns list of commit objects."""
+ last = self.repo[self.repo.head.target]
+ return list(self.repo.walk(last.id, GIT_SORT_TIME))
+
+ def add_commit(self, blob_content, file_path, parents=[],
+ ref='refs/heads/master'):
+ """Create a commit from blob_content and file_path."""
+ repo = self.repo
+
+ blob_oid = repo.create_blob(blob_content)
+ blob_entry = IndexEntry(file_path, blob_oid, GIT_FILEMODE_BLOB)
+ repo.index.add(blob_entry)
+ tree_id = repo.index.write_tree()
+ oid = repo.create_commit(ref,
+ self.author,
+ self.committer,
+ 'commit', tree_id, parents)
+ return oid
+
+ def stage(self, file_path):
+ """Stage a file and return a tree id."""
+ repo = self.repo
+ repo.index.add(file_path)
+ repo.index.write()
+ return repo.index.write_tree()
+
+ def generate_commits(self):
+ """Generate n number of commits."""
+ parents = []
+ for i in xrange(self.num_commits):
+ blob_content = b'commit {}'.format(i)
+ test_file = 'test.txt'
+ with open(os.path.join(self.repo_store, test_file), 'w') as f:
+ f.write(blob_content)
+
+ self.stage(test_file)
+
+ commit_oid = self.add_commit(blob_content, test_file, parents)
+ parents = [commit_oid]
+
+ def generate_tags(self):
+ """Generate n number of tags."""
+ repo = self.repo
+ oid = repo.head.get_object().oid
+ for i in xrange(self.num_tags):
+ repo.create_tag('tag{}'.format(i), oid, GIT_OBJ_COMMIT,
+ self.committer, 'tag message {}'.format(i))
+
+ def init_repo(self):
+ return init_repository(self.repo_store)
+
+ def build(self):
+ """Return a repo, optionally with generated commits and tags."""
+ if self.num_commits:
+ self.generate_commits()
+ if self.num_tags:
+ self.generate_tags()
+ return self.repo
=== modified file 'turnip/api/views.py'
--- turnip/api/views.py 2015-03-05 01:24:43 +0000
+++ turnip/api/views.py 2015-03-05 01:24:43 +0000
@@ -80,3 +80,28 @@
except Exception:
return exc.HTTPNotFound()
return json.dumps(ref)
+
+
+@resource(path='/repo/{name}/compare/{c1}..{c2}')
+class DiffAPI(object):
+ """Provides HTTP API for git references."""
+
+ def __init__(self, request):
+ config = TurnipConfig()
+ self.request = request
+ self.repo_store = config.get('repo_store')
+
+ @repo_path
+ def get(self):
+ """Returns diff of two commits."""
+ c1 = self.request.matchdict['c1']
+ c2 = self.request.matchdict['c2']
+ for sha in self.request.matchdict.iteritems():
+ if 'c' in sha[0] and not 7 <= len(sha[1]) <= 40:
+ return exc.HTTPBadRequest(
+ comment='invalid sha1: {}'.format(sha))
+ try:
+ patch = Store.get_diff(self.repo, c1, c2)
+ except:
+ return exc.HTTPNotFound()
+ return json.dumps(patch)
Follow ups