Diff comments:
> === modified file 'requirements.txt' > --- requirements.txt 2015-03-12 13:10:48 +0000 > +++ requirements.txt 2015-04-29 04:42:20 +0000 > @@ -1,3 +1,4 @@ > +contextlib2==0.4.0 > cornice==0.19.0 > lazr.sshserver==0.1.1 > PasteDeploy==1.5.2 > > === modified file 'setup.py' > --- setup.py 2015-04-24 09:37:22 +0000 > +++ setup.py 2015-04-29 04:42:20 +0000 > @@ -15,6 +15,7 @@ > README = f.read() > > requires = [ > + 'contextlib2', Done. > 'cornice', > 'lazr.sshserver', > # Should be 0.22.1 once released; for the time being we carry > cherry-picks. > > === modified file 'turnip/api/store.py' > --- turnip/api/store.py 2015-04-25 17:46:51 +0000 > +++ turnip/api/store.py 2015-04-29 04:42:20 +0000 > @@ -1,8 +1,13 @@ > # Copyright 2015 Canonical Ltd. All rights reserved. > > +from contextlib2 import ( > + contextmanager, > + ExitStack, > + ) > import itertools > import os > import shutil > +import uuid > > from pygit2 import ( > clone_repository, > @@ -63,6 +68,10 @@ > } > > > +def is_bare_repo(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 +79,25 @@ > return True > > > -def init_repo(repo_path, clone_from=None, clone_refs=False, 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_repo(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, clone_refs=False, > + alternate_repo_paths=None, is_bare=True): > """Initialise a new git repository or clone from existing.""" > assert is_valid_new_path(repo_path) > init_repository(repo_path, is_bare) > @@ -97,12 +124,37 @@ > for ref in from_repo.listall_references(): > to_repo.create_reference( > ref, from_repo.lookup_reference(ref).target) > + > + if alternate_repo_paths: > + write_alternates(repo_path, alternate_repo_paths) > return repo_path > > > -def open_repo(repo_path): > - """Open an existing git repository.""" > - return Repository(repo_path) > +@contextmanager > +def open_repo(repo_store, repo_name): > + """Open an existing git repository. Optionally create an ephemeral > + repository with alternates if repo_path contains ':'. > + > + :param repo_store: path to repository root. > + :param repo_name: repository name. > + """ > + if ':' in repo_name: > + try: > + # create ephemeral repo with alternates set from both > + repos = [os.path.join(repo_store, repo) > + for repo in repo_name.split(':')] > + tmp_repo_path = os.path.join(repo_store, > + 'ephemeral-' + uuid.uuid4().hex) > + ephemeral_repo_path = init_repo( > + tmp_repo_path, > + alternate_repo_paths=repos) > + repo = Repository(ephemeral_repo_path) > + yield repo > + finally: > + delete_repo(ephemeral_repo_path) > + else: > + repo_path = os.path.join(repo_store, repo_name) > + yield Repository(repo_path) > > > def delete_repo(repo_path): > @@ -110,32 +162,32 @@ > shutil.rmtree(repo_path) > > > -def get_refs(repo_path): > +def get_refs(repo_store, repo_name): > """Return all refs for a git repository.""" > - repo = open_repo(repo_path) > - refs = {} > - for ref in repo.listall_references(): > - git_object = repo.lookup_reference(ref).peel() > - # Filter non-unicode refs, as refs are treated as unicode > - # given json is unable to represent arbitrary byte strings. > - try: > - ref.decode('utf-8') > - except UnicodeDecodeError: > - pass > - else: > - refs.update(format_ref(ref, git_object)) > - return refs > - > - > -def get_ref(repo_path, ref): > + with open_repo(repo_store, repo_name) as repo: > + refs = {} > + for ref in repo.listall_references(): > + git_object = repo.lookup_reference(ref).peel() > + # Filter non-unicode refs, as refs are treated as unicode > + # given json is unable to represent arbitrary byte strings. > + try: > + ref.decode('utf-8') > + except UnicodeDecodeError: > + pass > + else: > + refs.update(format_ref(ref, git_object)) > + return refs > + > + > +def get_ref(repo_store, repo_name, ref): > """Return a specific ref for a git repository.""" > - repo = open_repo(repo_path) > - git_object = repo.lookup_reference(ref.encode('utf-8')).peel() > - ref_obj = format_ref(ref, git_object) > - return ref_obj > - > - > -def get_common_ancestor_diff(repo_path, sha1_target, sha1_source, > + with open_repo(repo_store, repo_name) as repo: > + git_object = repo.lookup_reference(ref.encode('utf-8')).peel() > + ref_obj = format_ref(ref, git_object) > + return ref_obj > + > + > +def get_common_ancestor_diff(repo_store, repo_name, sha1_target, sha1_source, > context_lines=3): > """Get diff of common ancestor and source diff. > > @@ -143,93 +195,98 @@ > :param sha1_source: source sha1 for merge base. > :param context_lines: num unchanged lines that define a hunk boundary. > """ > - repo = open_repo(repo_path) > - common_ancestor = repo.merge_base(sha1_target, sha1_source) > - return get_diff(repo_path, common_ancestor, sha1_source, context_lines) > - > - > -def get_merge_diff(repo_path, sha1_base, sha1_head, context_lines=3): > + with open_repo(repo_store, repo_name) as repo: > + common_ancestor = repo.merge_base(sha1_target, sha1_source) > + return get_diff(repo_store, repo_name, common_ancestor, > + sha1_source, context_lines) > + > + > +def get_merge_diff(repo_store, repo_name, sha1_base, > + sha1_head, context_lines=3): > """Get diff of common ancestor and source diff. > > :param sha1_base: target sha1 for merge. > :param sha1_head: source sha1 for merge. > :param context_lines: num unchanged lines that define a hunk boundary. > """ > - repo = open_repo(repo_path) > - merged_index = repo.merge_commits(sha1_base, sha1_head) > - conflicts = set() > - if merged_index.conflicts is not None: > - for conflict in list(merged_index.conflicts): > - path = [entry for entry in conflict if entry is not None][0].path > - conflicts.add(path) > - merged_file = repo.merge_file_from_index(*conflict) > - blob_oid = repo.create_blob(merged_file) > - merged_index.add(IndexEntry(path, blob_oid, GIT_FILEMODE_BLOB)) > - del merged_index.conflicts[path] > - diff = merged_index.diff_to_tree( > - repo[sha1_base].tree, context_lines=context_lines).patch > - shas = [sha1_base, sha1_head] > - commits = [get_commit(repo_path, sha, repo) for sha in shas] > - diff = {'commits': commits, 'patch': diff, 'conflicts': > sorted(conflicts)} > - return diff > - > - > -def get_diff(repo_path, sha1_from, sha1_to, context_lines=3): > + with open_repo(repo_store, repo_name) as repo: > + merged_index = repo.merge_commits(sha1_base, sha1_head) > + conflicts = set() > + if merged_index.conflicts is not None: > + for conflict in list(merged_index.conflicts): > + path = [entry for entry in conflict > + if entry is not None][0].path > + conflicts.add(path) > + merged_file = repo.merge_file_from_index(*conflict) > + blob_oid = repo.create_blob(merged_file) > + merged_index.add(IndexEntry(path, blob_oid, > GIT_FILEMODE_BLOB)) > + del merged_index.conflicts[path] > + diff = merged_index.diff_to_tree( > + repo[sha1_base].tree, context_lines=context_lines).patch > + shas = [sha1_base, sha1_head] > + commits = [get_commit(repo_store, repo_name, sha, repo) > + for sha in shas] > + diff = {'commits': commits, 'patch': diff, > + 'conflicts': sorted(conflicts)} > + return diff > + > + > +def get_diff(repo_store, repo_name, sha1_from, sha1_to, context_lines=3): > """Get patch and associated commits of two sha1s. > > :param sha1_from: diff from sha1. > :param sha1_to: diff to sha1. > :param context_lines: num unchanged lines that define a hunk boundary. > """ > - repo = open_repo(repo_path) > - shas = [sha1_from, sha1_to] > - commits = [get_commit(repo_path, sha, repo) for sha in shas] > - diff = { > - 'commits': commits, > - 'patch': repo.diff(commits[0]['sha1'], commits[1]['sha1'], > - False, 0, context_lines).patch > + with open_repo(repo_store, repo_name) as repo: > + shas = [sha1_from, sha1_to] > + commits = [get_commit(repo_store, repo_name, sha, repo) > + for sha in shas] > + diff = { > + 'commits': commits, > + 'patch': repo.diff(commits[0]['sha1'], commits[1]['sha1'], > + False, 0, context_lines).patch > } > - return diff > - > - > -def get_log(repo_path, start=None, limit=None, stop=None): > + return diff > + > + > +def get_log(repo_store, repo_name, start=None, limit=None, stop=None): > """Return a commit collection from HEAD or optionally a start oid. > > :param start: sha1 or branch to start listing commits from. > :param limit: limit number of commits to return. > :param stop: ignore a commit (and its ancestors). > """ > - repo = open_repo(repo_path) > - if not start: > - start = repo.head.target # walk from HEAD > - walker = repo.walk(start) > - if stop: > - walker.hide(stop) # filter stop sha1 and its ancestors > - if limit > 0: > - walker = itertools.islice(walker, limit) > - commits = [format_commit(commit) for commit in walker] > - return commits > - > - > -def get_commit(repo_path, commit_oid, repo=None): > + with open_repo(repo_store, repo_name) as repo: > + if not start: > + start = repo.head.target # walk from HEAD > + walker = repo.walk(start) > + if stop: > + walker.hide(stop) # filter stop sha1 and its ancestors > + if limit > 0: > + walker = itertools.islice(walker, limit) > + return [format_commit(commit) for commit in walker] > + > + > +def get_commit(repo_store, repo_name, commit_oid, repo=None): > """Return a single commit object from an oid.""" > - if not repo: > - repo = open_repo(repo_path) > - git_object = repo.get(commit_oid) > - if git_object is None: > - raise GitError('Object {} does not exist in repository {}.'.format( > - commit_oid, repo_path)) > - commit = format_commit(git_object) > - return commit > - > - > -def get_commits(repo_path, commit_oids): > + with ExitStack() as stack: > + if not repo: > + repo = stack.enter_context(open_repo(repo_store, repo_name)) > + git_object = repo.get(commit_oid) > + if git_object is None: > + raise GitError('Object {} does not exist in repository > {}.'.format( > + commit_oid, repo_name)) > + return format_commit(git_object) > + > + > +def get_commits(repo_store, repo_name, commit_oids): > """Return a collection of commit objects from a list of oids.""" > - repo = open_repo(repo_path) > - commits = [] > - for commit in commit_oids: > - try: > - commits.append(get_commit(repo_path, commit, repo)) > - except GitError: > - pass > - return commits > + with open_repo(repo_store, repo_name) as repo: > + commits = [] > + for commit in commit_oids: > + try: > + commits.append(get_commit(repo_store, repo_name, commit, > repo)) > + except GitError: > + pass > + return commits > > === modified file 'turnip/api/tests/test_api.py' > --- turnip/api/tests/test_api.py 2015-04-27 13:16:08 +0000 > +++ turnip/api/tests/test_api.py 2015-04-29 04:42:20 +0000 > @@ -74,6 +74,59 @@ > self.assertEqual(200, resp.status_code) > self.assertIn(new_repo_path, resp.json['repo_url']) > > + def test_cross_repo_merge_diff(self): > + """Merge diff can be requested across 2 repositories.""" > + factory = RepoFactory(self.repo_store) > + c1 = factory.add_commit('foo', 'foobar.txt') > + factory.set_head(c1) > + > + repo2_name = uuid.uuid4().hex > + factory2 = RepoFactory( > + os.path.join(self.repo_root, repo2_name), clone_from=factory) > + c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1]) > + c3 = factory2.add_commit('baz', 'foobar.txt', parents=[c1]) > + > + resp = self.app.get('/repo/{}:{}/compare-merge/{}:{}'.format( > + self.repo_path, repo2_name, c2, c3)) > + self.assertIn('-bar', resp.json['patch']) > + > + def test_cross_repo_diff(self): > + """Diff can be requested across 2 repositories.""" > + factory = RepoFactory(self.repo_store) > + c1 = factory.add_commit('foo', 'foobar.txt') > + factory.set_head(c1) > + > + repo2_name = uuid.uuid4().hex > + factory2 = RepoFactory( > + os.path.join(self.repo_root, repo2_name), clone_from=factory) > + c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1]) > + c3 = factory2.add_commit('baz', 'foobar.txt', parents=[c1]) > + > + resp = self.app.get('/repo/{}:{}/compare/{}..{}'.format( > + self.repo_path, repo2_name, c2, c3)) > + self.assertIn('-bar', resp.json['patch']) > + self.assertIn('+baz', resp.json['patch']) > + > + def test_cross_repo_diff_invalid_repo(self): > + """Cross repo diff with invalid repo returns HTTP 404.""" > + resp = self.app.get('/repo/1:2/compare-merge/3:4', > expect_errors=True) > + self.assertEqual(404, resp.status_code) > + > + def test_cross_repo_diff_invalid_commit(self): > + """Cross repo diff with an invalid commit returns HTTP 404.""" > + factory = RepoFactory(self.repo_store) > + c1 = factory.add_commit('foo', 'foobar.txt') > + factory.set_head(c1) > + > + repo2_name = uuid.uuid4().hex > + RepoFactory( > + os.path.join(self.repo_root, repo2_name), clone_from=factory) > + c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1]) > + > + resp = self.app.get('/repo/{}:{}/diff/{}:{}'.format( > + self.repo_path, repo2_name, c2, 'invalid'), expect_errors=True) > + self.assertEqual(404, resp.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)) > @@ -244,12 +297,15 @@ > """Ensure expected changes exist in diff patch.""" > repo = RepoFactory(self.repo_store) > c1 = repo.add_commit('foo\nbar\nbaz\n', 'blah.txt') > - c2_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt', > parents=[c1]) > - c3_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt', > parents=[c2_right]) > + c2_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt', > + parents=[c1]) > + c3_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt', > + parents=[c2_right]) > c2_left = repo.add_commit('foo\nbar\nbar\n', 'blah.txt', > parents=[c1]) > - c3_left = repo.add_commit('foo\nbar\nbar\n', 'blah.txt', > parents=[c2_left]) > + c3_left = repo.add_commit('foo\nbar\nbar\n', 'blah.txt', > + parents=[c2_left]) > > - resp = self.app.get('/repo/{}/compare-merge/{}/{}'.format( > + resp = self.app.get('/repo/{}/compare-merge/{}:{}'.format( > self.repo_path, c3_right, c3_left)) > self.assertIn(' quux', resp.json_body['patch']) > self.assertIn('-baz', resp.json_body['patch']) > @@ -264,7 +320,7 @@ > c2_left = repo.add_commit('foo\nbar\n', 'blah.txt', parents=[c1]) > c2_right = repo.add_commit('foo\nbaz\n', 'blah.txt', parents=[c1]) > > - resp = self.app.get('/repo/{}/compare-merge/{}/{}'.format( > + resp = self.app.get('/repo/{}/compare-merge/{}:{}'.format( > self.repo_path, c2_left, c2_right)) > self.assertIn(dedent("""\ > +<<<<<<< blah.txt > > === modified file 'turnip/api/tests/test_helpers.py' > --- turnip/api/tests/test_helpers.py 2015-04-14 15:12:31 +0000 > +++ turnip/api/tests/test_helpers.py 2015-04-29 04:42:20 +0000 > @@ -2,9 +2,12 @@ > > import itertools > import os > +import urllib > +import urlparse > import uuid > > from pygit2 import ( > + clone_repository, > init_repository, > GIT_FILEMODE_BLOB, > GIT_OBJ_COMMIT, > @@ -29,7 +32,7 @@ > """Builds a git repository in a user defined state.""" > > def __init__(self, repo_path=None, num_commits=None, > - num_branches=None, num_tags=None): > + num_branches=None, num_tags=None, clone_from=None): > self.author = Signature('Test Author', '[email protected]') > self.branches = [] > self.committer = Signature('Test Commiter', '[email protected]') > @@ -38,7 +41,10 @@ > self.num_commits = num_commits > self.num_tags = num_tags > self.repo_path = repo_path > - self.repo = self.init_repo() > + if clone_from: > + self.repo = self.clone_repo(clone_from) > + else: > + self.repo = self.init_repo() > > @property > def commits(self): > @@ -62,6 +68,9 @@ > blob_content, tree_id, parents) > return oid > > + def set_head(self, oid): > + self.repo.create_reference('refs/heads/master', oid) > + > def add_branch(self, name, oid): > commit = self.repo.get(oid) > branch = self.repo.create_branch('branch-{}'.format(name), commit) > @@ -133,6 +142,12 @@ > def init_repo(self): > return init_repository(self.repo_path) > > + def clone_repo(self, repo_factory): > + """Return a pygit2 repo object cloned from an existing factory > repo.""" > + clone_from_url = urlparse.urljoin( > + 'file:', urllib.pathname2url(repo_factory.repo.path)) > + return clone_repository(clone_from_url, self.repo_path) > + > def build(self): > """Return a repo, optionally with generated commits and tags.""" > if self.num_branches: > > === modified file 'turnip/api/tests/test_store.py' > --- turnip/api/tests/test_store.py 2015-04-25 12:50:07 +0000 > +++ turnip/api/tests/test_store.py 2015-04-29 04:42:20 +0000 > @@ -8,20 +8,29 @@ > > import os.path > import subprocess > +import uuid > > -from fixtures import TempDir > +from fixtures import ( > + EnvironmentVariable, > + TempDir, > + ) > import pygit2 > from testtools import TestCase > > from turnip.api import store > -from turnip.api.tests.test_helpers import RepoFactory > +from turnip.api.tests.test_helpers import ( > + open_repo, > + RepoFactory, > + ) > > > class InitTestCase(TestCase): > > def setUp(self): > super(InitTestCase, self).setUp() > - self.root = self.useFixture(TempDir()).path > + 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 assertAllLinkCounts(self, link_count, path): > count = 0 > @@ -44,19 +53,55 @@ > self.assertNotIn(absent, out) > > def makeOrig(self): > - self.orig_path = os.path.join(self.root, 'orig/') > + self.orig_path = os.path.join(self.repo_store, 'orig/') > orig = RepoFactory( > self.orig_path, num_branches=3, num_commits=2).build() > self.orig_refs = orig.listall_references() > self.master_oid = orig.lookup_reference('refs/heads/master').target > self.orig_objs = os.path.join(self.orig_path, '.git/objects') > > + def assert_alternate_exists(self, alternate_path, repo_path): > + """Assert alternate_path exists in alternates at repo_path.""" > + objects_path = '{}\n'.format( > + os.path.join(alternate_path, 'objects')) > + with open(store.alternates_path(repo_path), 'r') as alts: > + alts_content = alts.read() > + self.assertIn(objects_path, alts_content) > + > def test_from_scratch(self): > - path = os.path.join(self.root, 'repo/') > + path = os.path.join(self.repo_store, 'repo/') > self.assertEqual(path, store.init_repo(path)) > r = pygit2.Repository(path) > self.assertEqual([], r.listall_references()) > > + def test_open_ephemeral_repo(self): > + """Opening a repo where a repo name contains ':' should return > + a new ephemeral repo. > + """ > + repos = [uuid.uuid4().hex, uuid.uuid4().hex] > + repo_name = '{}:{}'.format(repos[0], repos[1]) > + alt_path = os.path.join(self.repo_store, repos[0]) > + with store.open_repo(self.repo_store, repo_name) as repo: > + self.assert_alternate_exists(alt_path, repo.path) > + > + 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 = store.init_repo( > + new_repo_path, alternate_repo_paths=[factory.repo.path]) > + self.assert_alternate_exists(factory.repo.path, repo_path_with_alt) > + > + 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 = store.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) > + > def test_clone_with_refs(self): > self.makeOrig() > self.assertAllLinkCounts(1, self.orig_objs) > @@ -64,7 +109,7 @@ > # init_repo with clone_from=orig and clone_refs=True creates a > # repo with the same set of refs. And the objects are copied > # too. > - to_path = os.path.join(self.root, 'to/') > + to_path = os.path.join(self.repo_store, 'to/') > store.init_repo(to_path, clone_from=self.orig_path, clone_refs=True) > to = pygit2.Repository(to_path) > self.assertIsNot(None, to[self.master_oid]) > @@ -88,7 +133,7 @@ > > # init_repo with clone_from=orig and clone_refs=False creates a > # repo without any refs, but the objects are copied. > - to_path = os.path.join(self.root, 'to/') > + to_path = os.path.join(self.repo_store, 'to/') > store.init_repo(to_path, clone_from=self.orig_path, clone_refs=False) > to = pygit2.Repository(to_path) > self.assertIsNot(None, to[self.master_oid]) > > === modified file 'turnip/api/views.py' > --- turnip/api/views.py 2015-04-27 13:16:08 +0000 > +++ turnip/api/views.py 2015-04-29 04:42:20 +0000 > @@ -17,9 +17,9 @@ > return > os.path.realpath(repo_path).startswith(os.path.realpath(repo_store)) > > > -def repo_path(func): > - """Decorator builds repo path from request name and repo_store.""" > - def repo_path_decorator(self): > +def validate_path(func): > + """Decorator validates repo path from request name and repo_store.""" > + def validate_path_decorator(self): > name = self.request.matchdict['name'] > if not name: > self.request.errors.add('body', 'name', 'repo name is missing') > @@ -28,9 +28,8 @@ > if not is_valid_path(self.repo_store, repo_path): > self.request.errors.add('body', 'name', 'invalid path.') > raise exc.HTTPInternalServerError() > - > - return func(self, repo_path) > - return repo_path_decorator > + return func(self, self.repo_store, name) > + return validate_path_decorator > > > class BaseAPI(object): > @@ -79,10 +78,11 @@ > except GitError: > return exc.HTTPConflict() # 409 > > - @repo_path > - def delete(self, repo_path): > + @validate_path > + def delete(self, repo_store, repo_name): > """Delete an existing git repository.""" > try: > + repo_path = os.path.join(repo_store, repo_name) > store.delete_repo(repo_path) > except (IOError, OSError): > return exc.HTTPNotFound() # 404 > @@ -97,18 +97,18 @@ > super(RefAPI, self).__init__() > self.request = request > > - @repo_path > - def collection_get(self, repo_path): > + @validate_path > + def collection_get(self, repo_store, repo_name): > try: > - return store.get_refs(repo_path) > + return store.get_refs(repo_store, repo_name) > except (KeyError, GitError): > return exc.HTTPNotFound() # 404 > > - @repo_path > - def get(self, repo_path): > + @validate_path > + def get(self, repo_store, repo_name): > ref = 'refs/' + self.request.matchdict['ref'] > try: > - return store.get_ref(repo_path, ref) > + return store.get_ref(repo_store, repo_name, ref) > except (KeyError, GitError): > return exc.HTTPNotFound() > > @@ -121,13 +121,14 @@ > Two dots provides a simple diff, equivalent to `git diff A B`. > Three dots provides the symmetric or common ancestor diff, equivalent > to `git diff $(git-merge-base A B) B`. > + {name} can be two : separated repositories, for a cross repository diff. > """ > def __init__(self, request): > super(DiffAPI, self).__init__() > self.request = request > > - @repo_path > - def get(self, repo_path): > + @validate_path > + def get(self, repo_store, repo_name): > """Returns diff of two commits.""" > commits = re.split('(\.{2,3})', self.request.matchdict['commits']) > context_lines = int(self.request.params.get('context_lines', 3)) > @@ -135,7 +136,8 @@ > return exc.HTTPBadRequest() > try: > diff_type = commits[1] > - args = (repo_path, commits[0], commits[2], context_lines) > + args = (repo_store, repo_name, commits[0], > + commits[2], context_lines) > if diff_type == '..': > patch = store.get_diff(*args) > elif diff_type == '...': > @@ -146,23 +148,24 @@ > return patch > > > -@resource(path='/repo/{name}/compare-merge/{base}/{head}') > +@resource(path='/repo/{name}/compare-merge/{base}:{head}') > class DiffMergeAPI(BaseAPI): > """Provides an HTTP API for merge previews. > > {head} will be merged into {base} and the diff from {base} returned. > + {name} can be two : separated repositories, for a cross repository diff. > """ > def __init__(self, request): > super(DiffMergeAPI, self).__init__() > self.request = request > > - @repo_path > - def get(self, repo_path): > + @validate_path > + def get(self, repo_store, repo_name): > """Returns diff of two commits.""" > context_lines = int(self.request.params.get('context_lines', 3)) > try: > patch = store.get_merge_diff( > - repo_path, self.request.matchdict['base'], > + repo_store, repo_name, self.request.matchdict['base'], > self.request.matchdict['head'], context_lines) > except (ValueError, GitError): > # invalid pygit2 sha1's return ValueError: 1: Ambiguous lookup > @@ -179,21 +182,21 @@ > super(CommitAPI, self).__init__() > self.request = request > > - @repo_path > - def get(self, repo_path): > + @validate_path > + def get(self, repo_store, repo_name): > commit_sha1 = self.request.matchdict['sha1'] > try: > - commit = store.get_commit(repo_path, commit_sha1) > + commit = store.get_commit(repo_store, repo_name, commit_sha1) > except GitError: > return exc.HTTPNotFound() > return commit > > - @repo_path > - def collection_post(self, repo_path): > + @validate_path > + def collection_post(self, repo_store, repo_name): > """Get commits in bulk.""" > commits = extract_json_data(self.request).get('commits') > try: > - commits = store.get_commits(repo_path, commits) > + commits = store.get_commits(repo_store, repo_name, commits) > except GitError: > return exc.HTTPNotFound() > return commits > @@ -207,15 +210,15 @@ > super(LogAPI, self).__init__() > self.request = request > > - @repo_path > - def get(self, repo_path): > + @validate_path > + def get(self, repo_store, repo_name): > """Get log by sha1, filtered by limit and stop.""" > sha1 = self.request.matchdict['sha1'] > limit = int(self.request.params.get('limit', -1)) > stop = self.request.params.get('stop') > > try: > - log = store.get_log(repo_path, sha1, limit, stop) > + log = store.get_log(repo_store, repo_name, sha1, limit, stop) > except GitError: > return exc.HTTPNotFound() > return log > -- https://code.launchpad.net/~blr/turnip/api-init-with-alternates/+merge/256715 Your team Launchpad code reviewers is subscribed to branch lp:turnip. _______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

