Show More
@@ -211,12 +211,12 b' class HgRemote(object):' | |||||
211 | if node['path'] == path: |
|
211 | if node['path'] == path: | |
212 | return memfilectx( |
|
212 | return memfilectx( | |
213 | _repo, |
|
213 | _repo, | |
|
214 | changectx=memctx, | |||
214 | path=node['path'], |
|
215 | path=node['path'], | |
215 | data=node['content'], |
|
216 | data=node['content'], | |
216 | islink=False, |
|
217 | islink=False, | |
217 | isexec=bool(node['mode'] & stat.S_IXUSR), |
|
218 | isexec=bool(node['mode'] & stat.S_IXUSR), | |
218 |
copied=False |
|
219 | copied=False) | |
219 | memctx=memctx) |
|
|||
220 |
|
220 | |||
221 | raise exceptions.AbortException( |
|
221 | raise exceptions.AbortException( | |
222 | "Given path haven't been marked as added, " |
|
222 | "Given path haven't been marked as added, " | |
@@ -454,9 +454,10 b' class HgRemote(object):' | |||||
454 | fctx = ctx.filectx(path) |
|
454 | fctx = ctx.filectx(path) | |
455 |
|
455 | |||
456 | result = [] |
|
456 | result = [] | |
457 |
for i, |
|
457 | for i, annotate_obj in enumerate(fctx.annotate(), 1): | |
458 |
ln_no = i |
|
458 | ln_no = i | |
459 |
sha = hex(a |
|
459 | sha = hex(annotate_obj.fctx.node()) | |
|
460 | content = annotate_obj.text | |||
460 | result.append((ln_no, sha, content)) |
|
461 | result.append((ln_no, sha, content)) | |
461 | return result |
|
462 | return result | |
462 |
|
463 | |||
@@ -533,10 +534,22 b' class HgRemote(object):' | |||||
533 |
|
534 | |||
534 | @reraise_safe_exceptions |
|
535 | @reraise_safe_exceptions | |
535 | def lookup(self, wire, revision, both): |
|
536 | def lookup(self, wire, revision, both): | |
536 | # TODO Paris: Ugly hack to "deserialize" long for msgpack |
|
537 | ||
537 | if isinstance(revision, float): |
|
|||
538 | revision = long(revision) |
|
|||
539 | repo = self._factory.repo(wire) |
|
538 | repo = self._factory.repo(wire) | |
|
539 | ||||
|
540 | if isinstance(revision, int): | |||
|
541 | # NOTE(marcink): | |||
|
542 | # since Mercurial doesn't support indexes properly | |||
|
543 | # we need to shift accordingly by one to get proper index, e.g | |||
|
544 | # repo[-1] => repo[-2] | |||
|
545 | # repo[0] => repo[-1] | |||
|
546 | # repo[1] => repo[2] we also never call repo[0] because | |||
|
547 | # it's actually second commit | |||
|
548 | if revision <= 0: | |||
|
549 | revision = revision + -1 | |||
|
550 | else: | |||
|
551 | revision = revision + 1 | |||
|
552 | ||||
540 | try: |
|
553 | try: | |
541 | ctx = repo[revision] |
|
554 | ctx = repo[revision] | |
542 | except RepoLookupError: |
|
555 | except RepoLookupError: |
@@ -36,15 +36,15 b' def patch_largefiles_capabilities():' | |||||
36 | lfproto = hgcompat.largefiles.proto |
|
36 | lfproto = hgcompat.largefiles.proto | |
37 | wrapper = _dynamic_capabilities_wrapper( |
|
37 | wrapper = _dynamic_capabilities_wrapper( | |
38 | lfproto, hgcompat.extensions.extensions) |
|
38 | lfproto, hgcompat.extensions.extensions) | |
39 | lfproto.capabilities = wrapper |
|
39 | lfproto._capabilities = wrapper | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | def _dynamic_capabilities_wrapper(lfproto, extensions): |
|
42 | def _dynamic_capabilities_wrapper(lfproto, extensions): | |
43 |
|
43 | |||
44 | wrapped_capabilities = lfproto.capabilities |
|
44 | wrapped_capabilities = lfproto._capabilities | |
45 | logger = logging.getLogger('vcsserver.hg') |
|
45 | logger = logging.getLogger('vcsserver.hg') | |
46 |
|
46 | |||
47 | def _dynamic_capabilities(repo, proto): |
|
47 | def _dynamic_capabilities(orig, repo, proto): | |
48 | """ |
|
48 | """ | |
49 | Adds dynamic behavior, so that the capability is only added if the |
|
49 | Adds dynamic behavior, so that the capability is only added if the | |
50 | extension is enabled in the current ui object. |
|
50 | extension is enabled in the current ui object. | |
@@ -52,10 +52,10 b' def _dynamic_capabilities_wrapper(lfprot' | |||||
52 | if 'largefiles' in dict(extensions(repo.ui)): |
|
52 | if 'largefiles' in dict(extensions(repo.ui)): | |
53 | logger.debug('Extension largefiles enabled') |
|
53 | logger.debug('Extension largefiles enabled') | |
54 | calc_capabilities = wrapped_capabilities |
|
54 | calc_capabilities = wrapped_capabilities | |
|
55 | return calc_capabilities(orig, repo, proto) | |||
55 | else: |
|
56 | else: | |
56 | logger.debug('Extension largefiles disabled') |
|
57 | logger.debug('Extension largefiles disabled') | |
57 | calc_capabilities = lfproto.capabilitiesorig |
|
58 | return orig(repo, proto) | |
58 | return calc_capabilities(repo, proto) |
|
|||
59 |
|
59 | |||
60 | return _dynamic_capabilities |
|
60 | return _dynamic_capabilities | |
61 |
|
61 |
@@ -164,7 +164,8 b' def _extras_from_ui(ui):' | |||||
164 | def _rev_range_hash(repo, node): |
|
164 | def _rev_range_hash(repo, node): | |
165 |
|
165 | |||
166 | commits = [] |
|
166 | commits = [] | |
167 | for rev in xrange(repo[node], len(repo)): |
|
167 | start = repo[node].rev() | |
|
168 | for rev in xrange(start, len(repo)): | |||
168 | ctx = repo[rev] |
|
169 | ctx = repo[rev] | |
169 | commit_id = mercurial.node.hex(ctx.node()) |
|
170 | commit_id = mercurial.node.hex(ctx.node()) | |
170 | branch = ctx.branch() |
|
171 | branch = ctx.branch() |
@@ -21,9 +21,9 b' import itertools' | |||||
21 |
|
21 | |||
22 | import mercurial |
|
22 | import mercurial | |
23 | import mercurial.error |
|
23 | import mercurial.error | |
|
24 | import mercurial.wireprotoserver | |||
24 | import mercurial.hgweb.common |
|
25 | import mercurial.hgweb.common | |
25 | import mercurial.hgweb.hgweb_mod |
|
26 | import mercurial.hgweb.hgweb_mod | |
26 | import mercurial.hgweb.protocol |
|
|||
27 | import webob.exc |
|
27 | import webob.exc | |
28 |
|
28 | |||
29 | from vcsserver import pygrack, exceptions, settings, git_lfs |
|
29 | from vcsserver import pygrack, exceptions, settings, git_lfs | |
@@ -73,14 +73,18 b' class HgWeb(mercurial.hgweb.hgweb_mod.hg' | |||||
73 |
|
73 | |||
74 | This may be called by multiple threads. |
|
74 | This may be called by multiple threads. | |
75 | """ |
|
75 | """ | |
76 |
|
|
76 | from mercurial.hgweb import request as requestmod | |
77 | gen = self.run_wsgi(req) |
|
77 | req = requestmod.parserequestfromenv(environ) | |
|
78 | res = requestmod.wsgiresponse(req, start_response) | |||
|
79 | gen = self.run_wsgi(req, res) | |||
78 |
|
80 | |||
79 | first_chunk = None |
|
81 | first_chunk = None | |
80 |
|
82 | |||
81 | try: |
|
83 | try: | |
82 | data = gen.next() |
|
84 | data = gen.next() | |
83 | def first_chunk(): yield data |
|
85 | ||
|
86 | def first_chunk(): | |||
|
87 | yield data | |||
84 | except StopIteration: |
|
88 | except StopIteration: | |
85 | pass |
|
89 | pass | |
86 |
|
90 | |||
@@ -88,17 +92,18 b' class HgWeb(mercurial.hgweb.hgweb_mod.hg' | |||||
88 | return itertools.chain(first_chunk(), gen) |
|
92 | return itertools.chain(first_chunk(), gen) | |
89 | return gen |
|
93 | return gen | |
90 |
|
94 | |||
91 | def _runwsgi(self, req, repo): |
|
95 | def _runwsgi(self, req, res, repo): | |
92 | cmd = req.form.get('cmd', [''])[0] |
|
|||
93 | if not mercurial.hgweb.protocol.iscmd(cmd): |
|
|||
94 | req.respond( |
|
|||
95 | mercurial.hgweb.common.ErrorResponse( |
|
|||
96 | mercurial.hgweb.common.HTTP_BAD_REQUEST), |
|
|||
97 | mercurial.hgweb.protocol.HGTYPE |
|
|||
98 | ) |
|
|||
99 | return [''] |
|
|||
100 |
|
96 | |||
101 | return super(HgWeb, self)._runwsgi(req, repo) |
|
97 | cmd = req.qsparams.get('cmd', '') | |
|
98 | if not mercurial.wireprotoserver.iscmd(cmd): | |||
|
99 | # NOTE(marcink): for unsupported commands, we return bad request | |||
|
100 | # internally from HG | |||
|
101 | from mercurial.hgweb.common import statusmessage | |||
|
102 | res.status = statusmessage(mercurial.hgweb.common.HTTP_BAD_REQUEST) | |||
|
103 | res.setbodybytes('') | |||
|
104 | return res.sendresponse() | |||
|
105 | ||||
|
106 | return super(HgWeb, self)._runwsgi(req, res, repo) | |||
102 |
|
107 | |||
103 |
|
108 | |||
104 | def make_hg_ui_from_config(repo_config): |
|
109 | def make_hg_ui_from_config(repo_config): |
@@ -28,56 +28,45 b' def test_patch_largefiles_capabilities_a' | |||||
28 | patched_capabilities): |
|
28 | patched_capabilities): | |
29 | lfproto = hgcompat.largefiles.proto |
|
29 | lfproto = hgcompat.largefiles.proto | |
30 | hgpatches.patch_largefiles_capabilities() |
|
30 | hgpatches.patch_largefiles_capabilities() | |
31 | assert lfproto.capabilities.func_name == '_dynamic_capabilities' |
|
31 | assert lfproto._capabilities.func_name == '_dynamic_capabilities' | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | def test_dynamic_capabilities_uses_original_function_if_not_enabled( |
|
34 | def test_dynamic_capabilities_uses_original_function_if_not_enabled( | |
35 |
stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities |
|
35 | stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities, | |
|
36 | orig_capabilities): | |||
36 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( |
|
37 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( | |
37 | hgcompat.largefiles.proto, stub_extensions) |
|
38 | hgcompat.largefiles.proto, stub_extensions) | |
38 |
|
39 | |||
39 | caps = dynamic_capabilities(stub_repo, stub_proto) |
|
40 | caps = dynamic_capabilities(orig_capabilities, stub_repo, stub_proto) | |
40 |
|
41 | |||
41 | stub_extensions.assert_called_once_with(stub_ui) |
|
42 | stub_extensions.assert_called_once_with(stub_ui) | |
42 | assert LARGEFILES_CAPABILITY not in caps |
|
43 | assert LARGEFILES_CAPABILITY not in caps | |
43 |
|
44 | |||
44 |
|
45 | |||
45 | def test_dynamic_capabilities_uses_updated_capabilitiesorig( |
|
|||
46 | stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities): |
|
|||
47 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( |
|
|||
48 | hgcompat.largefiles.proto, stub_extensions) |
|
|||
49 |
|
||||
50 | # This happens when the extension is loaded for the first time, important |
|
|||
51 | # to ensure that an updated function is correctly picked up. |
|
|||
52 | hgcompat.largefiles.proto.capabilitiesorig = mock.Mock( |
|
|||
53 | return_value='REPLACED') |
|
|||
54 |
|
||||
55 | caps = dynamic_capabilities(stub_repo, stub_proto) |
|
|||
56 | assert 'REPLACED' == caps |
|
|||
57 |
|
||||
58 |
|
||||
59 | def test_dynamic_capabilities_ignores_updated_capabilities( |
|
46 | def test_dynamic_capabilities_ignores_updated_capabilities( | |
60 |
stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities |
|
47 | stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities, | |
|
48 | orig_capabilities): | |||
61 | stub_extensions.return_value = [('largefiles', mock.Mock())] |
|
49 | stub_extensions.return_value = [('largefiles', mock.Mock())] | |
62 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( |
|
50 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( | |
63 | hgcompat.largefiles.proto, stub_extensions) |
|
51 | hgcompat.largefiles.proto, stub_extensions) | |
64 |
|
52 | |||
65 | # This happens when the extension is loaded for the first time, important |
|
53 | # This happens when the extension is loaded for the first time, important | |
66 | # to ensure that an updated function is correctly picked up. |
|
54 | # to ensure that an updated function is correctly picked up. | |
67 | hgcompat.largefiles.proto.capabilities = mock.Mock( |
|
55 | hgcompat.largefiles.proto._capabilities = mock.Mock( | |
68 | side_effect=Exception('Must not be called')) |
|
56 | side_effect=Exception('Must not be called')) | |
69 |
|
57 | |||
70 | dynamic_capabilities(stub_repo, stub_proto) |
|
58 | dynamic_capabilities(orig_capabilities, stub_repo, stub_proto) | |
71 |
|
59 | |||
72 |
|
60 | |||
73 | def test_dynamic_capabilities_uses_largefiles_if_enabled( |
|
61 | def test_dynamic_capabilities_uses_largefiles_if_enabled( | |
74 |
stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities |
|
62 | stub_repo, stub_proto, stub_ui, stub_extensions, patched_capabilities, | |
|
63 | orig_capabilities): | |||
75 | stub_extensions.return_value = [('largefiles', mock.Mock())] |
|
64 | stub_extensions.return_value = [('largefiles', mock.Mock())] | |
76 |
|
65 | |||
77 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( |
|
66 | dynamic_capabilities = hgpatches._dynamic_capabilities_wrapper( | |
78 | hgcompat.largefiles.proto, stub_extensions) |
|
67 | hgcompat.largefiles.proto, stub_extensions) | |
79 |
|
68 | |||
80 | caps = dynamic_capabilities(stub_repo, stub_proto) |
|
69 | caps = dynamic_capabilities(orig_capabilities, stub_repo, stub_proto) | |
81 |
|
70 | |||
82 | stub_extensions.assert_called_once_with(stub_ui) |
|
71 | stub_extensions.assert_called_once_with(stub_ui) | |
83 | assert LARGEFILES_CAPABILITY in caps |
|
72 | assert LARGEFILES_CAPABILITY in caps | |
@@ -94,15 +83,11 b' def patched_capabilities(request):' | |||||
94 | Patch in `capabilitiesorig` and restore both capability functions. |
|
83 | Patch in `capabilitiesorig` and restore both capability functions. | |
95 | """ |
|
84 | """ | |
96 | lfproto = hgcompat.largefiles.proto |
|
85 | lfproto = hgcompat.largefiles.proto | |
97 | orig_capabilities = lfproto.capabilities |
|
86 | orig_capabilities = lfproto._capabilities | |
98 | orig_capabilitiesorig = lfproto.capabilitiesorig |
|
|||
99 |
|
||||
100 | lfproto.capabilitiesorig = mock.Mock(return_value='ORIG') |
|
|||
101 |
|
87 | |||
102 | @request.addfinalizer |
|
88 | @request.addfinalizer | |
103 | def restore(): |
|
89 | def restore(): | |
104 | lfproto.capabilities = orig_capabilities |
|
90 | lfproto._capabilities = orig_capabilities | |
105 | lfproto.capabilitiesorig = orig_capabilitiesorig |
|
|||
106 |
|
91 | |||
107 |
|
92 | |||
108 | @pytest.fixture |
|
93 | @pytest.fixture | |
@@ -120,6 +105,15 b' def stub_proto(stub_ui):' | |||||
120 |
|
105 | |||
121 |
|
106 | |||
122 | @pytest.fixture |
|
107 | @pytest.fixture | |
|
108 | def orig_capabilities(): | |||
|
109 | from mercurial.wireprotov1server import wireprotocaps | |||
|
110 | ||||
|
111 | def _capabilities(repo, proto): | |||
|
112 | return wireprotocaps | |||
|
113 | return _capabilities | |||
|
114 | ||||
|
115 | ||||
|
116 | @pytest.fixture | |||
123 | def stub_ui(): |
|
117 | def stub_ui(): | |
124 | return hgcompat.ui.ui() |
|
118 | return hgcompat.ui.ui() | |
125 |
|
119 |
General Comments 0
You need to be logged in to leave comments.
Login now