##// END OF EJS Templates
caches: new cache implementation for remote functions
marcink -
r739:6b84a339 default
parent child Browse files
Show More
@@ -14,6 +14,7 b''
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17 import collections
18 import collections
18 import logging
19 import logging
19 import os
20 import os
@@ -39,7 +40,7 b' from dulwich.server import update_server'
39
40
40 from vcsserver import exceptions, settings, subprocessio
41 from vcsserver import exceptions, settings, subprocessio
41 from vcsserver.utils import safe_str
42 from vcsserver.utils import safe_str
42 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
43 from vcsserver.base import RepoFactory, obfuscate_qs
43 from vcsserver.hgcompat import (
44 from vcsserver.hgcompat import (
44 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
45 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
45 from vcsserver.git_lfs.lib import LFSOidStore
46 from vcsserver.git_lfs.lib import LFSOidStore
@@ -47,10 +48,19 b' from vcsserver.git_lfs.lib import LFSOid'
47 DIR_STAT = stat.S_IFDIR
48 DIR_STAT = stat.S_IFDIR
48 FILE_MODE = stat.S_IFMT
49 FILE_MODE = stat.S_IFMT
49 GIT_LINK = objects.S_IFGITLINK
50 GIT_LINK = objects.S_IFGITLINK
51 PEELED_REF_MARKER = '^{}'
52
50
53
51 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
52
55
53
56
57 def str_to_dulwich(value):
58 """
59 Dulwich 0.10.1a requires `unicode` objects to be passed in.
60 """
61 return value.decode(settings.WIRE_ENCODING)
62
63
54 def reraise_safe_exceptions(func):
64 def reraise_safe_exceptions(func):
55 """Converts Dulwich exceptions to something neutral."""
65 """Converts Dulwich exceptions to something neutral."""
56
66
@@ -111,19 +121,7 b' class GitFactory(RepoFactory):'
111 """
121 """
112 Get a repository instance for the given path.
122 Get a repository instance for the given path.
113 """
123 """
114 region = self._cache_region
124 return self._create_repo(wire, create, use_libgit2)
115 context = wire.get('context', None)
116 repo_path = wire.get('path', '')
117 context_uid = '{}'.format(context)
118 cache = wire.get('cache', True)
119 cache_on = context and cache
120
121 @region.conditional_cache_on_arguments(condition=cache_on)
122 def create_new_repo(_repo_type, _repo_path, _context_uid, _use_libgit2):
123 return self._create_repo(wire, create, use_libgit2)
124
125 repo = create_new_repo(self.repo_type, repo_path, context_uid, use_libgit2)
126 return repo
127
125
128 def repo_libgit2(self, wire):
126 def repo_libgit2(self, wire):
129 return self.repo(wire, use_libgit2=True)
127 return self.repo(wire, use_libgit2=True)
@@ -133,14 +131,15 b' class GitRemote(object):'
133
131
134 def __init__(self, factory):
132 def __init__(self, factory):
135 self._factory = factory
133 self._factory = factory
136 self.peeled_ref_marker = '^{}'
137 self._bulk_methods = {
134 self._bulk_methods = {
138 "date": self.date,
135 "date": self.date,
139 "author": self.author,
136 "author": self.author,
137 "branch": self.branch,
140 "message": self.message,
138 "message": self.message,
141 "parents": self.parents,
139 "parents": self.parents,
142 "_commit": self.revision,
140 "_commit": self.revision,
143 }
141 }
142 self.region = self._factory._cache_region
144
143
145 def _wire_to_config(self, wire):
144 def _wire_to_config(self, wire):
146 if 'config' in wire:
145 if 'config' in wire:
@@ -156,6 +155,23 b' class GitRemote(object):'
156 params.extend(['-c', 'http.sslCAinfo={}'.format(ssl_cert_dir)])
155 params.extend(['-c', 'http.sslCAinfo={}'.format(ssl_cert_dir)])
157 return params
156 return params
158
157
158 def _cache_on(self, wire):
159 context = wire.get('context', '')
160 context_uid = '{}'.format(context)
161 repo_id = wire.get('repo_id', '')
162 cache = wire.get('cache', True)
163 cache_on = context and cache
164 return cache_on, context_uid, repo_id
165
166 @reraise_safe_exceptions
167 def discover_git_version(self):
168 stdout, _ = self.run_git_command(
169 {}, ['--version'], _bare=True, _safe=True)
170 prefix = 'git version'
171 if stdout.startswith(prefix):
172 stdout = stdout[len(prefix):]
173 return stdout.strip()
174
159 @reraise_safe_exceptions
175 @reraise_safe_exceptions
160 def is_empty(self, wire):
176 def is_empty(self, wire):
161 repo_init = self._factory.repo_libgit2(wire)
177 repo_init = self._factory.repo_libgit2(wire)
@@ -184,17 +200,21 b' class GitRemote(object):'
184
200
185 @reraise_safe_exceptions
201 @reraise_safe_exceptions
186 def assert_correct_path(self, wire):
202 def assert_correct_path(self, wire):
187 try:
203 cache_on, context_uid, repo_id = self._cache_on(wire)
188 repo_init = self._factory.repo_libgit2(wire)
204 @self.region.conditional_cache_on_arguments(condition=cache_on)
189 with repo_init as repo:
205 def _assert_correct_path(_context_uid, _repo_id):
190 pass
206 try:
191 except pygit2.GitError:
207 repo_init = self._factory.repo_libgit2(wire)
192 path = wire.get('path')
208 with repo_init as repo:
193 tb = traceback.format_exc()
209 pass
194 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
210 except pygit2.GitError:
195 return False
211 path = wire.get('path')
212 tb = traceback.format_exc()
213 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
214 return False
196
215
197 return True
216 return True
217 return _assert_correct_path(context_uid, repo_id)
198
218
199 @reraise_safe_exceptions
219 @reraise_safe_exceptions
200 def bare(self, wire):
220 def bare(self, wire):
@@ -212,10 +232,16 b' class GitRemote(object):'
212
232
213 @reraise_safe_exceptions
233 @reraise_safe_exceptions
214 def blob_raw_length(self, wire, sha):
234 def blob_raw_length(self, wire, sha):
215 repo_init = self._factory.repo_libgit2(wire)
235 cache_on, context_uid, repo_id = self._cache_on(wire)
216 with repo_init as repo:
236 @self.region.conditional_cache_on_arguments(condition=cache_on)
217 blob = repo[sha]
237 def _blob_raw_length(_context_uid, _repo_id, _sha):
218 return blob.size
238
239 repo_init = self._factory.repo_libgit2(wire)
240 with repo_init as repo:
241 blob = repo[sha]
242 return blob.size
243
244 return _blob_raw_length(context_uid, repo_id, sha)
219
245
220 def _parse_lfs_pointer(self, raw_content):
246 def _parse_lfs_pointer(self, raw_content):
221
247
@@ -236,13 +262,19 b' class GitRemote(object):'
236
262
237 @reraise_safe_exceptions
263 @reraise_safe_exceptions
238 def is_large_file(self, wire, sha):
264 def is_large_file(self, wire, sha):
239 repo_init = self._factory.repo_libgit2(wire)
240 with repo_init as repo:
241 blob = repo[sha]
242 if blob.is_binary:
243 return {}
244
265
245 return self._parse_lfs_pointer(blob.data)
266 cache_on, context_uid, repo_id = self._cache_on(wire)
267 @self.region.conditional_cache_on_arguments(condition=cache_on)
268 def _is_large_file(_context_uid, _repo_id, _sha):
269 repo_init = self._factory.repo_libgit2(wire)
270 with repo_init as repo:
271 blob = repo[sha]
272 if blob.is_binary:
273 return {}
274
275 return self._parse_lfs_pointer(blob.data)
276
277 return _is_large_file(context_uid, repo_id, sha)
246
278
247 @reraise_safe_exceptions
279 @reraise_safe_exceptions
248 def in_largefiles_store(self, wire, oid):
280 def in_largefiles_store(self, wire, oid):
@@ -276,15 +308,21 b' class GitRemote(object):'
276
308
277 @reraise_safe_exceptions
309 @reraise_safe_exceptions
278 def bulk_request(self, wire, rev, pre_load):
310 def bulk_request(self, wire, rev, pre_load):
279 result = {}
311 cache_on, context_uid, repo_id = self._cache_on(wire)
280 for attr in pre_load:
312 @self.region.conditional_cache_on_arguments(condition=cache_on)
281 try:
313 def _bulk_request(_context_uid, _repo_id, _rev, _pre_load):
282 method = self._bulk_methods[attr]
314 result = {}
283 args = [wire, rev]
315 for attr in pre_load:
284 result[attr] = method(*args)
316 try:
285 except KeyError as e:
317 method = self._bulk_methods[attr]
286 raise exceptions.VcsException(e)("Unknown bulk attribute: %s" % attr)
318 args = [wire, rev]
287 return result
319 result[attr] = method(*args)
320 except KeyError as e:
321 raise exceptions.VcsException(e)(
322 "Unknown bulk attribute: %s" % attr)
323 return result
324
325 return _bulk_request(context_uid, repo_id, rev, sorted(pre_load))
288
326
289 def _build_opener(self, url):
327 def _build_opener(self, url):
290 handlers = []
328 handlers = []
@@ -371,6 +409,34 b' class GitRemote(object):'
371 index.build_index_from_tree(repo.path, repo.index_path(),
409 index.build_index_from_tree(repo.path, repo.index_path(),
372 repo.object_store, repo["HEAD"].tree)
410 repo.object_store, repo["HEAD"].tree)
373
411
412 @reraise_safe_exceptions
413 def branch(self, wire, commit_id):
414 cache_on, context_uid, repo_id = self._cache_on(wire)
415 cache_on = False
416 @self.region.conditional_cache_on_arguments(condition=cache_on)
417 def _branch(_context_uid, _repo_id, _commit_id):
418 regex = re.compile('^refs/heads')
419
420 def filter_with(ref):
421 return regex.match(ref[0]) and ref[1] == _commit_id
422
423 branches = filter(filter_with, self.get_refs(wire).items())
424 return [x[0].split('refs/heads/')[-1] for x in branches]
425
426 return _branch(context_uid, repo_id, commit_id)
427
428 @reraise_safe_exceptions
429 def commit_branches(self, wire, commit_id):
430 cache_on, context_uid, repo_id = self._cache_on(wire)
431 @self.region.conditional_cache_on_arguments(condition=cache_on)
432 def _commit_branches(_context_uid, _repo_id, _commit_id):
433 repo_init = self._factory.repo_libgit2(wire)
434 with repo_init as repo:
435 branches = [x for x in repo.branches.with_commit(_commit_id)]
436 return branches
437
438 return _commit_branches(context_uid, repo_id, commit_id)
439
374 # TODO: this is quite complex, check if that can be simplified
440 # TODO: this is quite complex, check if that can be simplified
375 @reraise_safe_exceptions
441 @reraise_safe_exceptions
376 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
442 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
@@ -510,7 +576,7 b' class GitRemote(object):'
510 # that contains a tag object, so that we would end up with
576 # that contains a tag object, so that we would end up with
511 # a peeled ref at this point.
577 # a peeled ref at this point.
512 for k in remote_refs:
578 for k in remote_refs:
513 if k.endswith(self.peeled_ref_marker):
579 if k.endswith(PEELED_REF_MARKER):
514 log.debug("Skipping peeled reference %s", k)
580 log.debug("Skipping peeled reference %s", k)
515 continue
581 continue
516 repo[k] = remote_refs[k]
582 repo[k] = remote_refs[k]
@@ -547,7 +613,7 b' class GitRemote(object):'
547 if ref in remote_refs:
613 if ref in remote_refs:
548 # duplicate, skip
614 # duplicate, skip
549 continue
615 continue
550 if ref.endswith(self.peeled_ref_marker):
616 if ref.endswith(PEELED_REF_MARKER):
551 log.debug("Skipping peeled reference %s", ref)
617 log.debug("Skipping peeled reference %s", ref)
552 continue
618 continue
553 # don't sync HEAD
619 # don't sync HEAD
@@ -579,7 +645,7 b' class GitRemote(object):'
579 if not self.check_url(url, wire):
645 if not self.check_url(url, wire):
580 return
646 return
581 config = self._wire_to_config(wire)
647 config = self._wire_to_config(wire)
582 repo = self._factory.repo(wire)
648 self._factory.repo(wire)
583 self.run_git_command(
649 self.run_git_command(
584 wire, ['push', url, '--mirror'], fail_on_stderr=False,
650 wire, ['push', url, '--mirror'], fail_on_stderr=False,
585 _copts=self._remote_conf(config),
651 _copts=self._remote_conf(config),
@@ -612,53 +678,80 b' class GitRemote(object):'
612
678
613 @reraise_safe_exceptions
679 @reraise_safe_exceptions
614 def get_object(self, wire, sha):
680 def get_object(self, wire, sha):
615 repo_init = self._factory.repo_libgit2(wire)
681
616 with repo_init as repo:
682 cache_on, context_uid, repo_id = self._cache_on(wire)
683 @self.region.conditional_cache_on_arguments(condition=cache_on)
684 def _get_object(_context_uid, _repo_id, _sha):
685 repo_init = self._factory.repo_libgit2(wire)
686 with repo_init as repo:
617
687
618 missing_commit_err = 'Commit {} does not exist for `{}`'.format(sha, wire['path'])
688 missing_commit_err = 'Commit {} does not exist for `{}`'.format(sha, wire['path'])
619 try:
689 try:
620 commit = repo.revparse_single(sha)
690 commit = repo.revparse_single(sha)
621 except (KeyError, ValueError) as e:
691 except (KeyError, ValueError) as e:
622 raise exceptions.LookupException(e)(missing_commit_err)
692 raise exceptions.LookupException(e)(missing_commit_err)
623
693
624 if isinstance(commit, pygit2.Tag):
694 if isinstance(commit, pygit2.Tag):
625 commit = repo.get(commit.target)
695 commit = repo.get(commit.target)
626
696
627 # check for dangling commit
697 # check for dangling commit
628 branches = [x for x in repo.branches.with_commit(commit.hex)]
698 branches = [x for x in repo.branches.with_commit(commit.hex)]
629 if not branches:
699 if not branches:
630 raise exceptions.LookupException(None)(missing_commit_err)
700 raise exceptions.LookupException(None)(missing_commit_err)
701
702 commit_id = commit.hex
703 type_id = commit.type
631
704
632 commit_id = commit.hex
705 return {
633 type_id = commit.type
706 'id': commit_id,
707 'type': self._type_id_to_name(type_id),
708 'commit_id': commit_id,
709 'idx': 0
710 }
634
711
635 return {
712 return _get_object(context_uid, repo_id, sha)
636 'id': commit_id,
637 'type': self._type_id_to_name(type_id),
638 'commit_id': commit_id,
639 'idx': 0
640 }
641
713
642 @reraise_safe_exceptions
714 @reraise_safe_exceptions
643 def get_refs(self, wire):
715 def get_refs(self, wire):
644 repo_init = self._factory.repo_libgit2(wire)
716 cache_on, context_uid, repo_id = self._cache_on(wire)
645 with repo_init as repo:
717 @self.region.conditional_cache_on_arguments(condition=cache_on)
646 result = {}
718 def _get_refs(_context_uid, _repo_id):
647 for ref in repo.references:
719
648 peeled_sha = repo.lookup_reference(ref).peel()
720 repo_init = self._factory.repo_libgit2(wire)
649 result[ref] = peeled_sha.hex
721 with repo_init as repo:
722 regex = re.compile('^refs/(heads|tags)/')
723 return {x.name: x.target.hex for x in
724 filter(lambda ref: regex.match(ref.name) ,repo.listall_reference_objects())}
725
726 return _get_refs(context_uid, repo_id)
650
727
651 return result
728 @reraise_safe_exceptions
729 def get_branch_pointers(self, wire):
730 cache_on, context_uid, repo_id = self._cache_on(wire)
731 @self.region.conditional_cache_on_arguments(condition=cache_on)
732 def _get_branch_pointers(_context_uid, _repo_id):
733
734 repo_init = self._factory.repo_libgit2(wire)
735 regex = re.compile('^refs/heads')
736 with repo_init as repo:
737 branches = filter(lambda ref: regex.match(ref.name), repo.listall_reference_objects())
738 return {x.target.hex: x.shorthand for x in branches}
739
740 return _get_branch_pointers(context_uid, repo_id)
652
741
653 @reraise_safe_exceptions
742 @reraise_safe_exceptions
654 def head(self, wire, show_exc=True):
743 def head(self, wire, show_exc=True):
655 repo_init = self._factory.repo_libgit2(wire)
744 cache_on, context_uid, repo_id = self._cache_on(wire)
656 with repo_init as repo:
745 @self.region.conditional_cache_on_arguments(condition=cache_on)
657 try:
746 def _head(_context_uid, _repo_id, _show_exc):
658 return repo.head.peel().hex
747 repo_init = self._factory.repo_libgit2(wire)
659 except Exception:
748 with repo_init as repo:
660 if show_exc:
749 try:
661 raise
750 return repo.head.peel().hex
751 except Exception:
752 if show_exc:
753 raise
754 return _head(context_uid, repo_id, show_exc)
662
755
663 @reraise_safe_exceptions
756 @reraise_safe_exceptions
664 def init(self, wire):
757 def init(self, wire):
@@ -672,17 +765,22 b' class GitRemote(object):'
672
765
673 @reraise_safe_exceptions
766 @reraise_safe_exceptions
674 def revision(self, wire, rev):
767 def revision(self, wire, rev):
675 repo_init = self._factory.repo_libgit2(wire)
676 with repo_init as repo:
677 commit = repo[rev]
678 obj_data = {
679 'id': commit.id.hex,
680 }
681 # tree objects itself don't have tree_id attribute
682 if hasattr(commit, 'tree_id'):
683 obj_data['tree'] = commit.tree_id.hex
684
768
685 return obj_data
769 cache_on, context_uid, repo_id = self._cache_on(wire)
770 @self.region.conditional_cache_on_arguments(condition=cache_on)
771 def _revision(_context_uid, _repo_id, _rev):
772 repo_init = self._factory.repo_libgit2(wire)
773 with repo_init as repo:
774 commit = repo[rev]
775 obj_data = {
776 'id': commit.id.hex,
777 }
778 # tree objects itself don't have tree_id attribute
779 if hasattr(commit, 'tree_id'):
780 obj_data['tree'] = commit.tree_id.hex
781
782 return obj_data
783 return _revision(context_uid, repo_id, rev)
686
784
687 @reraise_safe_exceptions
785 @reraise_safe_exceptions
688 def date(self, wire, rev):
786 def date(self, wire, rev):
@@ -711,10 +809,14 b' class GitRemote(object):'
711
809
712 @reraise_safe_exceptions
810 @reraise_safe_exceptions
713 def parents(self, wire, rev):
811 def parents(self, wire, rev):
714 repo_init = self._factory.repo_libgit2(wire)
812 cache_on, context_uid, repo_id = self._cache_on(wire)
715 with repo_init as repo:
813 @self.region.conditional_cache_on_arguments(condition=cache_on)
716 commit = repo[rev]
814 def _parents(_context_uid, _repo_id, _rev):
717 return [x.hex for x in commit.parent_ids]
815 repo_init = self._factory.repo_libgit2(wire)
816 with repo_init as repo:
817 commit = repo[rev]
818 return [x.hex for x in commit.parent_ids]
819 return _parents(context_uid, repo_id, rev)
718
820
719 @reraise_safe_exceptions
821 @reraise_safe_exceptions
720 def set_refs(self, wire, key, value):
822 def set_refs(self, wire, key, value):
@@ -758,39 +860,49 b' class GitRemote(object):'
758
860
759 @reraise_safe_exceptions
861 @reraise_safe_exceptions
760 def tree_and_type_for_path(self, wire, commit_id, path):
862 def tree_and_type_for_path(self, wire, commit_id, path):
761 repo_init = self._factory.repo_libgit2(wire)
863
864 cache_on, context_uid, repo_id = self._cache_on(wire)
865 @self.region.conditional_cache_on_arguments(condition=cache_on)
866 def _tree_and_type_for_path(_context_uid, _repo_id, _commit_id, _path):
867 repo_init = self._factory.repo_libgit2(wire)
762
868
763 with repo_init as repo:
869 with repo_init as repo:
764 commit = repo[commit_id]
870 commit = repo[commit_id]
765 try:
871 try:
766 tree = commit.tree[path]
872 tree = commit.tree[path]
767 except KeyError:
873 except KeyError:
768 return None, None, None
874 return None, None, None
769
875
770 return tree.id.hex, tree.type, tree.filemode
876 return tree.id.hex, tree.type, tree.filemode
877 return _tree_and_type_for_path(context_uid, repo_id, commit_id, path)
771
878
772 @reraise_safe_exceptions
879 @reraise_safe_exceptions
773 def tree_items(self, wire, tree_id):
880 def tree_items(self, wire, tree_id):
774 repo_init = self._factory.repo_libgit2(wire)
881
882 cache_on, context_uid, repo_id = self._cache_on(wire)
883 @self.region.conditional_cache_on_arguments(condition=cache_on)
884 def _tree_items(_context_uid, _repo_id, _tree_id):
775
885
776 with repo_init as repo:
886 repo_init = self._factory.repo_libgit2(wire)
777 try:
887 with repo_init as repo:
778 tree = repo[tree_id]
888 try:
779 except KeyError:
889 tree = repo[tree_id]
780 raise ObjectMissing('No tree with id: {}'.format(tree_id))
890 except KeyError:
891 raise ObjectMissing('No tree with id: {}'.format(tree_id))
781
892
782 result = []
893 result = []
783 for item in tree:
894 for item in tree:
784 item_sha = item.hex
895 item_sha = item.hex
785 item_mode = item.filemode
896 item_mode = item.filemode
786 item_type = item.type
897 item_type = item.type
787
898
788 if item_type == 'commit':
899 if item_type == 'commit':
789 # NOTE(marcink): submodules we translate to 'link' for backward compat
900 # NOTE(marcink): submodules we translate to 'link' for backward compat
790 item_type = 'link'
901 item_type = 'link'
791
902
792 result.append((item.name, item_mode, item_sha, item_type))
903 result.append((item.name, item_mode, item_sha, item_type))
793 return result
904 return result
905 return _tree_items(context_uid, repo_id, tree_id)
794
906
795 @reraise_safe_exceptions
907 @reraise_safe_exceptions
796 def update_server_info(self, wire):
908 def update_server_info(self, wire):
@@ -798,24 +910,20 b' class GitRemote(object):'
798 update_server_info(repo)
910 update_server_info(repo)
799
911
800 @reraise_safe_exceptions
912 @reraise_safe_exceptions
801 def discover_git_version(self):
802 stdout, _ = self.run_git_command(
803 {}, ['--version'], _bare=True, _safe=True)
804 prefix = 'git version'
805 if stdout.startswith(prefix):
806 stdout = stdout[len(prefix):]
807 return stdout.strip()
808
809 @reraise_safe_exceptions
810 def get_all_commit_ids(self, wire):
913 def get_all_commit_ids(self, wire):
811
914
812 cmd = ['rev-list', '--reverse', '--date-order', '--branches', '--tags']
915 cache_on, context_uid, repo_id = self._cache_on(wire)
813 try:
916 @self.region.conditional_cache_on_arguments(condition=cache_on)
814 output, __ = self.run_git_command(wire, cmd)
917 def _get_all_commit_ids(_context_uid, _repo_id):
815 return output.splitlines()
918
816 except Exception:
919 cmd = ['rev-list', '--reverse', '--date-order', '--branches', '--tags']
817 # Can be raised for empty repositories
920 try:
818 return []
921 output, __ = self.run_git_command(wire, cmd)
922 return output.splitlines()
923 except Exception:
924 # Can be raised for empty repositories
925 return []
926 return _get_all_commit_ids(context_uid, repo_id)
819
927
820 @reraise_safe_exceptions
928 @reraise_safe_exceptions
821 def run_git_command(self, wire, cmd, **opts):
929 def run_git_command(self, wire, cmd, **opts):
@@ -870,22 +978,17 b' class GitRemote(object):'
870 @reraise_safe_exceptions
978 @reraise_safe_exceptions
871 def install_hooks(self, wire, force=False):
979 def install_hooks(self, wire, force=False):
872 from vcsserver.hook_utils import install_git_hooks
980 from vcsserver.hook_utils import install_git_hooks
873 repo = self._factory.repo(wire)
981 bare = self.bare(wire)
874 return install_git_hooks(repo.path, repo.bare, force_create=force)
982 path = wire['path']
983 return install_git_hooks(path, bare, force_create=force)
875
984
876 @reraise_safe_exceptions
985 @reraise_safe_exceptions
877 def get_hooks_info(self, wire):
986 def get_hooks_info(self, wire):
878 from vcsserver.hook_utils import (
987 from vcsserver.hook_utils import (
879 get_git_pre_hook_version, get_git_post_hook_version)
988 get_git_pre_hook_version, get_git_post_hook_version)
880 repo = self._factory.repo(wire)
989 bare = self.bare(wire)
990 path = wire['path']
881 return {
991 return {
882 'pre_version': get_git_pre_hook_version(repo.path, repo.bare),
992 'pre_version': get_git_pre_hook_version(path, bare),
883 'post_version': get_git_post_hook_version(repo.path, repo.bare),
993 'post_version': get_git_post_hook_version(path, bare),
884 }
994 }
885
886
887 def str_to_dulwich(value):
888 """
889 Dulwich 0.10.1a requires `unicode` objects to be passed in.
890 """
891 return value.decode(settings.WIRE_ENCODING)
@@ -147,25 +147,13 b' class MercurialFactory(RepoFactory):'
147 """
147 """
148 Get a repository instance for the given path.
148 Get a repository instance for the given path.
149 """
149 """
150 region = self._cache_region
150 return self._create_repo(wire, create)
151 context = wire.get('context', None)
152 repo_path = wire.get('path', '')
153 context_uid = '{}'.format(context)
154 cache = wire.get('cache', True)
155 cache_on = context and cache
156
157 @region.conditional_cache_on_arguments(condition=cache_on)
158 def create_new_repo(_repo_type, _repo_path, _context_uid):
159 return self._create_repo(wire, create)
160
161 return create_new_repo(self.repo_type, repo_path, context_uid)
162
151
163
152
164 class HgRemote(object):
153 class HgRemote(object):
165
154
166 def __init__(self, factory):
155 def __init__(self, factory):
167 self._factory = factory
156 self._factory = factory
168
169 self._bulk_methods = {
157 self._bulk_methods = {
170 "affected_files": self.ctx_files,
158 "affected_files": self.ctx_files,
171 "author": self.ctx_user,
159 "author": self.ctx_user,
@@ -180,10 +168,19 b' class HgRemote(object):'
180 "hidden": self.ctx_hidden,
168 "hidden": self.ctx_hidden,
181 "_file_paths": self.ctx_list,
169 "_file_paths": self.ctx_list,
182 }
170 }
171 self.region = self._factory._cache_region
183
172
184 def _get_ctx(self, repo, ref):
173 def _get_ctx(self, repo, ref):
185 return get_ctx(repo, ref)
174 return get_ctx(repo, ref)
186
175
176 def _cache_on(self, wire):
177 context = wire.get('context', '')
178 context_uid = '{}'.format(context)
179 repo_id = wire.get('repo_id', '')
180 cache = wire.get('cache', True)
181 cache_on = context and cache
182 return cache_on, context_uid, repo_id
183
187 @reraise_safe_exceptions
184 @reraise_safe_exceptions
188 def discover_hg_version(self):
185 def discover_hg_version(self):
189 from mercurial import util
186 from mercurial import util
@@ -217,33 +214,48 b' class HgRemote(object):'
217
214
218 @reraise_safe_exceptions
215 @reraise_safe_exceptions
219 def bookmarks(self, wire):
216 def bookmarks(self, wire):
220 repo = self._factory.repo(wire)
217 cache_on, context_uid, repo_id = self._cache_on(wire)
221 return dict(repo._bookmarks)
218 @self.region.conditional_cache_on_arguments(condition=cache_on)
219 def _bookmarks(_context_uid, _repo_id):
220 repo = self._factory.repo(wire)
221 return dict(repo._bookmarks)
222
223 return _bookmarks(context_uid, repo_id)
222
224
223 @reraise_safe_exceptions
225 @reraise_safe_exceptions
224 def branches(self, wire, normal, closed):
226 def branches(self, wire, normal, closed):
225 repo = self._factory.repo(wire)
227 cache_on, context_uid, repo_id = self._cache_on(wire)
226 iter_branches = repo.branchmap().iterbranches()
228 @self.region.conditional_cache_on_arguments(condition=cache_on)
227 bt = {}
229 def _branches(_context_uid, _repo_id, _normal, _closed):
228 for branch_name, _heads, tip, is_closed in iter_branches:
230 repo = self._factory.repo(wire)
229 if normal and not is_closed:
231 iter_branches = repo.branchmap().iterbranches()
230 bt[branch_name] = tip
232 bt = {}
231 if closed and is_closed:
233 for branch_name, _heads, tip, is_closed in iter_branches:
232 bt[branch_name] = tip
234 if normal and not is_closed:
235 bt[branch_name] = tip
236 if closed and is_closed:
237 bt[branch_name] = tip
233
238
234 return bt
239 return bt
240
241 return _branches(context_uid, repo_id, normal, closed)
235
242
236 @reraise_safe_exceptions
243 @reraise_safe_exceptions
237 def bulk_request(self, wire, rev, pre_load):
244 def bulk_request(self, wire, rev, pre_load):
238 result = {}
245 cache_on, context_uid, repo_id = self._cache_on(wire)
239 for attr in pre_load:
246 @self.region.conditional_cache_on_arguments(condition=cache_on)
240 try:
247 def _bulk_request(_context_uid, _repo_id, _rev, _pre_load):
241 method = self._bulk_methods[attr]
248 result = {}
242 result[attr] = method(wire, rev)
249 for attr in pre_load:
243 except KeyError as e:
250 try:
244 raise exceptions.VcsException(e)(
251 method = self._bulk_methods[attr]
245 'Unknown bulk attribute: "%s"' % attr)
252 result[attr] = method(wire, rev)
246 return result
253 except KeyError as e:
254 raise exceptions.VcsException(e)(
255 'Unknown bulk attribute: "%s"' % attr)
256 return result
257
258 return _bulk_request(context_uid, repo_id, rev, sorted(pre_load))
247
259
248 @reraise_safe_exceptions
260 @reraise_safe_exceptions
249 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
261 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
@@ -251,9 +263,8 b' class HgRemote(object):'
251 clone(baseui, source, dest, noupdate=not update_after_clone)
263 clone(baseui, source, dest, noupdate=not update_after_clone)
252
264
253 @reraise_safe_exceptions
265 @reraise_safe_exceptions
254 def commitctx(
266 def commitctx(self, wire, message, parents, commit_time, commit_timezone,
255 self, wire, message, parents, commit_time, commit_timezone,
267 user, files, extra, removed, updated):
256 user, files, extra, removed, updated):
257
268
258 repo = self._factory.repo(wire)
269 repo = self._factory.repo(wire)
259 baseui = self._factory._create_config(wire['config'])
270 baseui = self._factory._create_config(wire['config'])
@@ -284,7 +295,7 b' class HgRemote(object):'
284 data=node['content'],
295 data=node['content'],
285 islink=False,
296 islink=False,
286 isexec=bool(node['mode'] & stat.S_IXUSR),
297 isexec=bool(node['mode'] & stat.S_IXUSR),
287 copied=False)
298 copysource=False)
288
299
289 raise exceptions.AbortException()(
300 raise exceptions.AbortException()(
290 "Given path haven't been marked as added, "
301 "Given path haven't been marked as added, "
@@ -309,15 +320,14 b' class HgRemote(object):'
309
320
310 @reraise_safe_exceptions
321 @reraise_safe_exceptions
311 def ctx_branch(self, wire, revision):
322 def ctx_branch(self, wire, revision):
312 repo = self._factory.repo(wire)
313 ctx = self._get_ctx(repo, revision)
314 return ctx.branch()
315
323
316 @reraise_safe_exceptions
324 cache_on, context_uid, repo_id = self._cache_on(wire)
317 def ctx_children(self, wire, revision):
325 @self.region.conditional_cache_on_arguments(condition=cache_on)
318 repo = self._factory.repo(wire)
326 def _ctx_branch(_context_uid, _repo_id, _revision):
319 ctx = self._get_ctx(repo, revision)
327 repo = self._factory.repo(wire)
320 return [child.rev() for child in ctx.children()]
328 ctx = self._get_ctx(repo, revision)
329 return ctx.branch()
330 return _ctx_branch(context_uid, repo_id, revision)
321
331
322 @reraise_safe_exceptions
332 @reraise_safe_exceptions
323 def ctx_date(self, wire, revision):
333 def ctx_date(self, wire, revision):
@@ -333,9 +343,15 b' class HgRemote(object):'
333
343
334 @reraise_safe_exceptions
344 @reraise_safe_exceptions
335 def ctx_files(self, wire, revision):
345 def ctx_files(self, wire, revision):
336 repo = self._factory.repo(wire)
346
337 ctx = self._get_ctx(repo, revision)
347 cache_on, context_uid, repo_id = self._cache_on(wire)
338 return ctx.files()
348 @self.region.conditional_cache_on_arguments(condition=cache_on)
349 def _ctx_files(_context_uid, _repo_id, _revision):
350 repo = self._factory.repo(wire)
351 ctx = self._get_ctx(repo, revision)
352 return ctx.files()
353
354 return _ctx_files(context_uid, repo_id, revision)
339
355
340 @reraise_safe_exceptions
356 @reraise_safe_exceptions
341 def ctx_list(self, path, revision):
357 def ctx_list(self, path, revision):
@@ -345,9 +361,27 b' class HgRemote(object):'
345
361
346 @reraise_safe_exceptions
362 @reraise_safe_exceptions
347 def ctx_parents(self, wire, revision):
363 def ctx_parents(self, wire, revision):
348 repo = self._factory.repo(wire)
364 cache_on, context_uid, repo_id = self._cache_on(wire)
349 ctx = self._get_ctx(repo, revision)
365 @self.region.conditional_cache_on_arguments(condition=cache_on)
350 return [parent.rev() for parent in ctx.parents()]
366 def _ctx_parents(_context_uid, _repo_id, _revision):
367 repo = self._factory.repo(wire)
368 ctx = self._get_ctx(repo, revision)
369 return [parent.rev() for parent in ctx.parents()
370 if not (parent.hidden() or parent.obsolete())]
371
372 return _ctx_parents(context_uid, repo_id, revision)
373
374 @reraise_safe_exceptions
375 def ctx_children(self, wire, revision):
376 cache_on, context_uid, repo_id = self._cache_on(wire)
377 @self.region.conditional_cache_on_arguments(condition=cache_on)
378 def _ctx_children(_context_uid, _repo_id, _revision):
379 repo = self._factory.repo(wire)
380 ctx = self._get_ctx(repo, revision)
381 return [child.rev() for child in ctx.children()
382 if not (child.hidden() or child.obsolete())]
383
384 return _ctx_children(context_uid, repo_id, revision)
351
385
352 @reraise_safe_exceptions
386 @reraise_safe_exceptions
353 def ctx_phase(self, wire, revision):
387 def ctx_phase(self, wire, revision):
@@ -456,9 +490,7 b' class HgRemote(object):'
456 return True
490 return True
457
491
458 @reraise_safe_exceptions
492 @reraise_safe_exceptions
459 def diff(
493 def diff(self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews, context):
460 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
461 context):
462 repo = self._factory.repo(wire)
494 repo = self._factory.repo(wire)
463
495
464 if file_filter:
496 if file_filter:
@@ -527,7 +559,7 b' class HgRemote(object):'
527 return result
559 return result
528
560
529 @reraise_safe_exceptions
561 @reraise_safe_exceptions
530 def fctx_data(self, wire, revision, path):
562 def fctx_node_data(self, wire, revision, path):
531 repo = self._factory.repo(wire)
563 repo = self._factory.repo(wire)
532 ctx = self._get_ctx(repo, revision)
564 ctx = self._get_ctx(repo, revision)
533 fctx = ctx.filectx(path)
565 fctx = ctx.filectx(path)
@@ -549,10 +581,14 b' class HgRemote(object):'
549
581
550 @reraise_safe_exceptions
582 @reraise_safe_exceptions
551 def get_all_commit_ids(self, wire, name):
583 def get_all_commit_ids(self, wire, name):
552 repo = self._factory.repo(wire)
584 cache_on, context_uid, repo_id = self._cache_on(wire)
553 repo = repo.filtered(name)
585 @self.region.conditional_cache_on_arguments(condition=cache_on)
554 revs = map(lambda x: hex(x[7]), repo.changelog.index)
586 def _get_all_commit_ids(_context_uid, _repo_id, _name):
555 return revs
587 repo = self._factory.repo(wire)
588 repo = repo.filtered(name)
589 revs = map(lambda x: hex(x[7]), repo.changelog.index)
590 return revs
591 return _get_all_commit_ids(context_uid, repo_id, name)
556
592
557 @reraise_safe_exceptions
593 @reraise_safe_exceptions
558 def get_config_value(self, wire, section, name, untrusted=False):
594 def get_config_value(self, wire, section, name, untrusted=False):
@@ -571,7 +607,12 b' class HgRemote(object):'
571
607
572 @reraise_safe_exceptions
608 @reraise_safe_exceptions
573 def is_large_file(self, wire, path):
609 def is_large_file(self, wire, path):
574 return largefiles.lfutil.isstandin(path)
610 cache_on, context_uid, repo_id = self._cache_on(wire)
611 @self.region.conditional_cache_on_arguments(condition=cache_on)
612 def _is_large_file(_context_uid, _repo_id, _path):
613 return largefiles.lfutil.isstandin(path)
614
615 return _is_large_file(context_uid, repo_id, path)
575
616
576 @reraise_safe_exceptions
617 @reraise_safe_exceptions
577 def in_largefiles_store(self, wire, sha):
618 def in_largefiles_store(self, wire, sha):
@@ -600,31 +641,36 b' class HgRemote(object):'
600
641
601 @reraise_safe_exceptions
642 @reraise_safe_exceptions
602 def lookup(self, wire, revision, both):
643 def lookup(self, wire, revision, both):
603
644 cache_on, context_uid, repo_id = self._cache_on(wire)
604 repo = self._factory.repo(wire)
645 @self.region.conditional_cache_on_arguments(condition=cache_on)
646 def _lookup(_context_uid, _repo_id, _revision, _both):
605
647
606 if isinstance(revision, int):
648 repo = self._factory.repo(wire)
607 # NOTE(marcink):
649 rev = _revision
608 # since Mercurial doesn't support negative indexes properly
650 if isinstance(rev, int):
609 # we need to shift accordingly by one to get proper index, e.g
651 # NOTE(marcink):
610 # repo[-1] => repo[-2]
652 # since Mercurial doesn't support negative indexes properly
611 # repo[0] => repo[-1]
653 # we need to shift accordingly by one to get proper index, e.g
612 if revision <= 0:
654 # repo[-1] => repo[-2]
613 revision = revision + -1
655 # repo[0] => repo[-1]
614 try:
656 if rev <= 0:
615 ctx = self._get_ctx(repo, revision)
657 rev = rev + -1
616 except (TypeError, RepoLookupError) as e:
658 try:
617 e._org_exc_tb = traceback.format_exc()
659 ctx = self._get_ctx(repo, rev)
618 raise exceptions.LookupException(e)(revision)
660 except (TypeError, RepoLookupError) as e:
619 except LookupError as e:
661 e._org_exc_tb = traceback.format_exc()
620 e._org_exc_tb = traceback.format_exc()
662 raise exceptions.LookupException(e)(rev)
621 raise exceptions.LookupException(e)(e.name)
663 except LookupError as e:
664 e._org_exc_tb = traceback.format_exc()
665 raise exceptions.LookupException(e)(e.name)
622
666
623 if not both:
667 if not both:
624 return ctx.hex()
668 return ctx.hex()
625
669
626 ctx = repo[ctx.hex()]
670 ctx = repo[ctx.hex()]
627 return ctx.hex(), ctx.rev()
671 return ctx.hex(), ctx.rev()
672
673 return _lookup(context_uid, repo_id, revision, both)
628
674
629 @reraise_safe_exceptions
675 @reraise_safe_exceptions
630 def pull(self, wire, url, commit_ids=None):
676 def pull(self, wire, url, commit_ids=None):
@@ -667,10 +713,15 b' class HgRemote(object):'
667 return ctx.rev()
713 return ctx.rev()
668
714
669 @reraise_safe_exceptions
715 @reraise_safe_exceptions
670 def rev_range(self, wire, filter):
716 def rev_range(self, wire, commit_filter):
671 repo = self._factory.repo(wire)
717 cache_on, context_uid, repo_id = self._cache_on(wire)
672 revisions = [rev for rev in revrange(repo, filter)]
718 @self.region.conditional_cache_on_arguments(condition=cache_on)
673 return revisions
719 def _rev_range(_context_uid, _repo_id, _filter):
720 repo = self._factory.repo(wire)
721 revisions = [rev for rev in revrange(repo, commit_filter)]
722 return revisions
723
724 return _rev_range(context_uid, repo_id, sorted(commit_filter))
674
725
675 @reraise_safe_exceptions
726 @reraise_safe_exceptions
676 def rev_range_hash(self, wire, node):
727 def rev_range_hash(self, wire, node):
@@ -724,8 +775,7 b' class HgRemote(object):'
724 return output.getvalue()
775 return output.getvalue()
725
776
726 @reraise_safe_exceptions
777 @reraise_safe_exceptions
727 def tag(self, wire, name, revision, message, local, user,
778 def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone):
728 tag_time, tag_timezone):
729 repo = self._factory.repo(wire)
779 repo = self._factory.repo(wire)
730 ctx = self._get_ctx(repo, revision)
780 ctx = self._get_ctx(repo, revision)
731 node = ctx.node()
781 node = ctx.node()
@@ -740,8 +790,13 b' class HgRemote(object):'
740
790
741 @reraise_safe_exceptions
791 @reraise_safe_exceptions
742 def tags(self, wire):
792 def tags(self, wire):
743 repo = self._factory.repo(wire)
793 cache_on, context_uid, repo_id = self._cache_on(wire)
744 return repo.tags()
794 @self.region.conditional_cache_on_arguments(condition=cache_on)
795 def _tags(_context_uid, _repo_id):
796 repo = self._factory.repo(wire)
797 return repo.tags()
798
799 return _tags(context_uid, repo_id)
745
800
746 @reraise_safe_exceptions
801 @reraise_safe_exceptions
747 def update(self, wire, node=None, clean=False):
802 def update(self, wire, node=None, clean=False):
@@ -762,8 +817,7 b' class HgRemote(object):'
762 return output.getvalue()
817 return output.getvalue()
763
818
764 @reraise_safe_exceptions
819 @reraise_safe_exceptions
765 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
820 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None, hooks=True):
766 hooks=True):
767 repo = self._factory.repo(wire)
821 repo = self._factory.repo(wire)
768 baseui = self._factory._create_config(wire['config'], hooks=hooks)
822 baseui = self._factory._create_config(wire['config'], hooks=hooks)
769
823
@@ -806,8 +860,7 b' class HgRemote(object):'
806 return hex(a)
860 return hex(a)
807
861
808 @reraise_safe_exceptions
862 @reraise_safe_exceptions
809 def push(self, wire, revisions, dest_path, hooks=True,
863 def push(self, wire, revisions, dest_path, hooks=True, push_branches=False):
810 push_branches=False):
811 repo = self._factory.repo(wire)
864 repo = self._factory.repo(wire)
812 baseui = self._factory._create_config(wire['config'], hooks=hooks)
865 baseui = self._factory._create_config(wire['config'], hooks=hooks)
813 commands.push(baseui, repo, dest=dest_path, rev=revisions,
866 commands.push(baseui, repo, dest=dest_path, rev=revisions,
@@ -846,7 +899,6 b' class HgRemote(object):'
846 repo.ui.setconfig('ui', 'username', username)
899 repo.ui.setconfig('ui', 'username', username)
847 commands.commit(baseui, repo, message=message, close_branch=close_branch)
900 commands.commit(baseui, repo, message=message, close_branch=close_branch)
848
901
849
850 @reraise_safe_exceptions
902 @reraise_safe_exceptions
851 def rebase(self, wire, source=None, dest=None, abort=False):
903 def rebase(self, wire, source=None, dest=None, abort=False):
852 repo = self._factory.repo(wire)
904 repo = self._factory.repo(wire)
@@ -98,19 +98,7 b' class SubversionFactory(RepoFactory):'
98 """
98 """
99 Get a repository instance for the given path.
99 Get a repository instance for the given path.
100 """
100 """
101 region = self._cache_region
101 return self._create_repo(wire, create, compatible_version)
102 context = wire.get('context', None)
103 repo_path = wire.get('path', '')
104 context_uid = '{}'.format(context)
105 cache = wire.get('cache', True)
106 cache_on = context and cache
107
108 @region.conditional_cache_on_arguments(condition=cache_on)
109 def create_new_repo(_repo_type, _repo_path, _context_uid, compatible_version_id):
110 return self._create_repo(wire, create, compatible_version)
111
112 return create_new_repo(self.repo_type, repo_path, context_uid,
113 compatible_version)
114
102
115
103
116 NODE_TYPE_MAPPING = {
104 NODE_TYPE_MAPPING = {
@@ -126,6 +114,15 b' class SvnRemote(object):'
126 # TODO: Remove once we do not use internal Mercurial objects anymore
114 # TODO: Remove once we do not use internal Mercurial objects anymore
127 # for subversion
115 # for subversion
128 self._hg_factory = hg_factory
116 self._hg_factory = hg_factory
117 self.region = self._factory._cache_region
118
119 def _cache_on(self, wire):
120 context = wire.get('context', '')
121 context_uid = '{}'.format(context)
122 repo_id = wire.get('repo_id', '')
123 cache = wire.get('cache', True)
124 cache_on = context and cache
125 return cache_on, context_uid, repo_id
129
126
130 @reraise_safe_exceptions
127 @reraise_safe_exceptions
131 def discover_svn_version(self):
128 def discover_svn_version(self):
@@ -138,7 +135,6 b' class SvnRemote(object):'
138
135
139 @reraise_safe_exceptions
136 @reraise_safe_exceptions
140 def is_empty(self, wire):
137 def is_empty(self, wire):
141 repo = self._factory.repo(wire)
142
138
143 try:
139 try:
144 return self.lookup(wire, -1) == 0
140 return self.lookup(wire, -1) == 0
@@ -216,9 +212,14 b' class SvnRemote(object):'
216 return start_rev, end_rev
212 return start_rev, end_rev
217
213
218 def revision_properties(self, wire, revision):
214 def revision_properties(self, wire, revision):
219 repo = self._factory.repo(wire)
215
220 fs_ptr = svn.repos.fs(repo)
216 cache_on, context_uid, repo_id = self._cache_on(wire)
221 return svn.fs.revision_proplist(fs_ptr, revision)
217 @self.region.conditional_cache_on_arguments(condition=cache_on)
218 def _revision_properties(_context_uid, _repo_id, _revision):
219 repo = self._factory.repo(wire)
220 fs_ptr = svn.repos.fs(repo)
221 return svn.fs.revision_proplist(fs_ptr, revision)
222 return _revision_properties(context_uid, repo_id, revision)
222
223
223 def revision_changes(self, wire, revision):
224 def revision_changes(self, wire, revision):
224
225
@@ -264,22 +265,27 b' class SvnRemote(object):'
264 }
265 }
265 return changes
266 return changes
266
267
268 @reraise_safe_exceptions
267 def node_history(self, wire, path, revision, limit):
269 def node_history(self, wire, path, revision, limit):
268 cross_copies = False
270 cache_on, context_uid, repo_id = self._cache_on(wire)
269 repo = self._factory.repo(wire)
271 @self.region.conditional_cache_on_arguments(condition=cache_on)
270 fsobj = svn.repos.fs(repo)
272 def _assert_correct_path(_context_uid, _repo_id, _path, _revision, _limit):
271 rev_root = svn.fs.revision_root(fsobj, revision)
273 cross_copies = False
274 repo = self._factory.repo(wire)
275 fsobj = svn.repos.fs(repo)
276 rev_root = svn.fs.revision_root(fsobj, revision)
272
277
273 history_revisions = []
278 history_revisions = []
274 history = svn.fs.node_history(rev_root, path)
279 history = svn.fs.node_history(rev_root, path)
275 history = svn.fs.history_prev(history, cross_copies)
276 while history:
277 __, node_revision = svn.fs.history_location(history)
278 history_revisions.append(node_revision)
279 if limit and len(history_revisions) >= limit:
280 break
281 history = svn.fs.history_prev(history, cross_copies)
280 history = svn.fs.history_prev(history, cross_copies)
282 return history_revisions
281 while history:
282 __, node_revision = svn.fs.history_location(history)
283 history_revisions.append(node_revision)
284 if limit and len(history_revisions) >= limit:
285 break
286 history = svn.fs.history_prev(history, cross_copies)
287 return history_revisions
288 return _assert_correct_path(context_uid, repo_id, path, revision, limit)
283
289
284 def node_properties(self, wire, path, revision):
290 def node_properties(self, wire, path, revision):
285 repo = self._factory.repo(wire)
291 repo = self._factory.repo(wire)
@@ -314,27 +320,37 b' class SvnRemote(object):'
314
320
315 return annotations
321 return annotations
316
322
317 def get_node_type(self, wire, path, rev=None):
323 def get_node_type(self, wire, path, revision=None):
318 repo = self._factory.repo(wire)
324
319 fs_ptr = svn.repos.fs(repo)
325 cache_on, context_uid, repo_id = self._cache_on(wire)
320 if rev is None:
326 @self.region.conditional_cache_on_arguments(condition=cache_on)
321 rev = svn.fs.youngest_rev(fs_ptr)
327 def _get_node_type(_context_uid, _repo_id, _path, _revision):
322 root = svn.fs.revision_root(fs_ptr, rev)
328 repo = self._factory.repo(wire)
323 node = svn.fs.check_path(root, path)
329 fs_ptr = svn.repos.fs(repo)
324 return NODE_TYPE_MAPPING.get(node, None)
330 if _revision is None:
331 _revision = svn.fs.youngest_rev(fs_ptr)
332 root = svn.fs.revision_root(fs_ptr, _revision)
333 node = svn.fs.check_path(root, path)
334 return NODE_TYPE_MAPPING.get(node, None)
335 return _get_node_type(context_uid, repo_id, path, revision)
325
336
326 def get_nodes(self, wire, path, revision=None):
337 def get_nodes(self, wire, path, revision=None):
327 repo = self._factory.repo(wire)
338
328 fsobj = svn.repos.fs(repo)
339 cache_on, context_uid, repo_id = self._cache_on(wire)
329 if revision is None:
340 @self.region.conditional_cache_on_arguments(condition=cache_on)
330 revision = svn.fs.youngest_rev(fsobj)
341 def _get_nodes(_context_uid, _repo_id, _path, _revision):
331 root = svn.fs.revision_root(fsobj, revision)
342 repo = self._factory.repo(wire)
332 entries = svn.fs.dir_entries(root, path)
343 fsobj = svn.repos.fs(repo)
333 result = []
344 if _revision is None:
334 for entry_path, entry_info in entries.iteritems():
345 _revision = svn.fs.youngest_rev(fsobj)
335 result.append(
346 root = svn.fs.revision_root(fsobj, _revision)
336 (entry_path, NODE_TYPE_MAPPING.get(entry_info.kind, None)))
347 entries = svn.fs.dir_entries(root, path)
337 return result
348 result = []
349 for entry_path, entry_info in entries.iteritems():
350 result.append(
351 (entry_path, NODE_TYPE_MAPPING.get(entry_info.kind, None)))
352 return result
353 return _get_nodes(context_uid, repo_id, path, revision)
338
354
339 def get_file_content(self, wire, path, rev=None):
355 def get_file_content(self, wire, path, rev=None):
340 repo = self._factory.repo(wire)
356 repo = self._factory.repo(wire)
@@ -346,13 +362,18 b' class SvnRemote(object):'
346 return content.read()
362 return content.read()
347
363
348 def get_file_size(self, wire, path, revision=None):
364 def get_file_size(self, wire, path, revision=None):
349 repo = self._factory.repo(wire)
365
350 fsobj = svn.repos.fs(repo)
366 cache_on, context_uid, repo_id = self._cache_on(wire)
351 if revision is None:
367 @self.region.conditional_cache_on_arguments(condition=cache_on)
352 revision = svn.fs.youngest_revision(fsobj)
368 def _get_file_size(_context_uid, _repo_id, _path, _revision):
353 root = svn.fs.revision_root(fsobj, revision)
369 repo = self._factory.repo(wire)
354 size = svn.fs.file_length(root, path)
370 fsobj = svn.repos.fs(repo)
355 return size
371 if _revision is None:
372 _revision = svn.fs.youngest_revision(fsobj)
373 root = svn.fs.revision_root(fsobj, _revision)
374 size = svn.fs.file_length(root, path)
375 return size
376 return _get_file_size(context_uid, repo_id, path, revision)
356
377
357 def create_repository(self, wire, compatible_version=None):
378 def create_repository(self, wire, compatible_version=None):
358 log.info('Creating Subversion repository in path "%s"', wire['path'])
379 log.info('Creating Subversion repository in path "%s"', wire['path'])
@@ -61,7 +61,7 b' class TestGitFetch(object):'
61
61
62 with patch('dulwich.client.LocalGitClient.fetch') as mock_fetch:
62 with patch('dulwich.client.LocalGitClient.fetch') as mock_fetch:
63 mock_fetch.side_effect = side_effect
63 mock_fetch.side_effect = side_effect
64 self.remote_git.pull(wire=None, url='/tmp/', apply_refs=False)
64 self.remote_git.pull(wire={}, url='/tmp/', apply_refs=False)
65 determine_wants = self.mock_repo.object_store.determine_wants_all
65 determine_wants = self.mock_repo.object_store.determine_wants_all
66 determine_wants.assert_called_once_with(SAMPLE_REFS)
66 determine_wants.assert_called_once_with(SAMPLE_REFS)
67
67
@@ -79,7 +79,7 b' class TestGitFetch(object):'
79 with patch('dulwich.client.LocalGitClient.fetch') as mock_fetch:
79 with patch('dulwich.client.LocalGitClient.fetch') as mock_fetch:
80 mock_fetch.side_effect = side_effect
80 mock_fetch.side_effect = side_effect
81 self.remote_git.pull(
81 self.remote_git.pull(
82 wire=None, url='/tmp/', apply_refs=False,
82 wire={}, url='/tmp/', apply_refs=False,
83 refs=selected_refs.keys())
83 refs=selected_refs.keys())
84 determine_wants = self.mock_repo.object_store.determine_wants_all
84 determine_wants = self.mock_repo.object_store.determine_wants_all
85 assert determine_wants.call_count == 0
85 assert determine_wants.call_count == 0
@@ -95,7 +95,7 b' class TestGitFetch(object):'
95
95
96 with patch('vcsserver.git.Repo', create=False) as mock_repo:
96 with patch('vcsserver.git.Repo', create=False) as mock_repo:
97 mock_repo().get_refs.return_value = sample_refs
97 mock_repo().get_refs.return_value = sample_refs
98 remote_refs = remote_git.get_remote_refs(wire=None, url=url)
98 remote_refs = remote_git.get_remote_refs(wire={}, url=url)
99 mock_repo().get_refs.assert_called_once_with()
99 mock_repo().get_refs.assert_called_once_with()
100 assert remote_refs == sample_refs
100 assert remote_refs == sample_refs
101
101
@@ -26,36 +26,17 b' from mock import Mock, MagicMock, patch'
26 from vcsserver import exceptions, hg, hgcompat
26 from vcsserver import exceptions, hg, hgcompat
27
27
28
28
29 class TestHGLookup(object):
30 def setup(self):
31 self.mock_repo = MagicMock()
32 self.mock_repo.__getitem__.side_effect = LookupError(
33 'revision_or_commit_id', 'index', 'message')
34 factory = Mock()
35 factory.repo = Mock(return_value=self.mock_repo)
36 self.remote_hg = hg.HgRemote(factory)
37
38 def test_fail_lookup_hg(self):
39 with pytest.raises(Exception) as exc_info:
40 self.remote_hg.lookup(
41 wire=None, revision='revision_or_commit_id', both=True)
42
43 assert exc_info.value._vcs_kind == 'lookup'
44 assert 'revision_or_commit_id' in exc_info.value.args
45
46
47 class TestDiff(object):
29 class TestDiff(object):
48 def test_raising_safe_exception_when_lookup_failed(self):
30 def test_raising_safe_exception_when_lookup_failed(self):
49 repo = Mock()
31
50 factory = Mock()
32 factory = Mock()
51 factory.repo = Mock(return_value=repo)
52 hg_remote = hg.HgRemote(factory)
33 hg_remote = hg.HgRemote(factory)
53 with patch('mercurial.patch.diff') as diff_mock:
34 with patch('mercurial.patch.diff') as diff_mock:
54 diff_mock.side_effect = LookupError(
35 diff_mock.side_effect = LookupError(
55 'deadbeef', 'index', 'message')
36 'deadbeef', 'index', 'message')
56 with pytest.raises(Exception) as exc_info:
37 with pytest.raises(Exception) as exc_info:
57 hg_remote.diff(
38 hg_remote.diff(
58 wire=None, rev1='deadbeef', rev2='deadbee1',
39 wire={}, rev1='deadbeef', rev2='deadbee1',
59 file_filter=None, opt_git=True, opt_ignorews=True,
40 file_filter=None, opt_git=True, opt_ignorews=True,
60 context=3)
41 context=3)
61 assert type(exc_info.value) == Exception
42 assert type(exc_info.value) == Exception
@@ -45,8 +45,10 b" INVALID_CERTIFICATE_STDERR = '\\n'.join(["
45 reason="SVN not packaged for Cygwin")
45 reason="SVN not packaged for Cygwin")
46 def test_import_remote_repository_certificate_error(stderr, expected_reason):
46 def test_import_remote_repository_certificate_error(stderr, expected_reason):
47 from vcsserver import svn
47 from vcsserver import svn
48 factory = mock.Mock()
49 factory.repo = mock.Mock(return_value=mock.Mock())
48
50
49 remote = svn.SvnRemote(None)
51 remote = svn.SvnRemote(factory)
50 remote.is_path_valid_repository = lambda wire, path: True
52 remote.is_path_valid_repository = lambda wire, path: True
51
53
52 with mock.patch('subprocess.Popen',
54 with mock.patch('subprocess.Popen',
@@ -76,7 +78,10 b' def test_svn_libraries_can_be_imported()'
76 def test_username_password_extraction_from_url(example_url, parts):
78 def test_username_password_extraction_from_url(example_url, parts):
77 from vcsserver import svn
79 from vcsserver import svn
78
80
79 remote = svn.SvnRemote(None)
81 factory = mock.Mock()
82 factory.repo = mock.Mock(return_value=mock.Mock())
83
84 remote = svn.SvnRemote(factory)
80 remote.is_path_valid_repository = lambda wire, path: True
85 remote.is_path_valid_repository = lambda wire, path: True
81
86
82 assert remote.get_url_and_credentials(example_url) == parts
87 assert remote.get_url_and_credentials(example_url) == parts
General Comments 0
You need to be logged in to leave comments. Login now