Mercurial > hg
annotate hgext/largefiles/proto.py @ 15524:e7119b091809
merge with crew
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 17 Nov 2011 23:02:18 -0600 |
parents | a5a6a9b7f3b9 |
children | f15c646bffc7 |
rev | line source |
---|---|
15168 | 1 # Copyright 2011 Fog Creek Software |
2 # | |
3 # This software may be used and distributed according to the terms of the | |
4 # GNU General Public License version 2 or any later version. | |
5 | |
6 import os | |
7 import urllib2 | |
8 | |
9 from mercurial import error, httprepo, util, wireproto | |
10 from mercurial.i18n import _ | |
11 | |
12 import lfutil | |
13 | |
15255
7ab05d752405
largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents:
15252
diff
changeset
|
14 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.' |
7ab05d752405
largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents:
15252
diff
changeset
|
15 '\n\nPlease enable it in your Mercurial config ' |
7ab05d752405
largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents:
15252
diff
changeset
|
16 'file.\n') |
15168 | 17 |
18 def putlfile(repo, proto, sha): | |
15317
41f371150ccb
largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents:
15316
diff
changeset
|
19 '''Put a largefile into a repository's local store and into the |
41f371150ccb
largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents:
15316
diff
changeset
|
20 user cache.''' |
15168 | 21 proto.redirect() |
15391
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
22 |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
23 fd, tmpname = lfutil.mkstemp(repo, prefix='hg-putlfile') |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
24 tmpfp = os.fdopen(fd, 'wb+') |
15168 | 25 try: |
26 try: | |
15391
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
27 proto.getfile(tmpfp) |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
28 tmpfp.seek(0) |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
29 if sha != lfutil.hexsha1(tmpfp): |
15168 | 30 return wireproto.pushres(1) |
15391
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
31 tmpfp.close() |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
32 lfutil.copytostoreabsolute(repo, tmpname, sha) |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
33 except IOError, e: |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
34 repo.ui.warn(_('largefiles: failed to put %s (%s) into store: %s') % |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
35 (sha, tmpname, e.strerror)) |
15168 | 36 return wireproto.pushres(1) |
37 finally: | |
15391
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
38 tmpfp.close() |
a5a6a9b7f3b9
largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents:
15317
diff
changeset
|
39 os.unlink(tmpname) |
15168 | 40 |
41 return wireproto.pushres(0) | |
42 | |
43 def getlfile(repo, proto, sha): | |
15252
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
44 '''Retrieve a largefile from the repository-local cache or system |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
45 cache.''' |
15168 | 46 filename = lfutil.findfile(repo, sha) |
47 if not filename: | |
48 raise util.Abort(_('requested largefile %s not present in cache') % sha) | |
49 f = open(filename, 'rb') | |
50 length = os.fstat(f.fileno())[6] | |
15252
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
51 |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
52 # Since we can't set an HTTP content-length header here, and |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
53 # Mercurial core provides no way to give the length of a streamres |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
54 # (and reading the entire file into RAM would be ill-advised), we |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
55 # just send the length on the first line of the response, like the |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
56 # ssh proto does for string responses. |
15168 | 57 def generator(): |
58 yield '%d\n' % length | |
59 for chunk in f: | |
60 yield chunk | |
61 return wireproto.streamres(generator()) | |
62 | |
63 def statlfile(repo, proto, sha): | |
15252
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
64 '''Return '2\n' if the largefile is missing, '1\n' if it has a |
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
65 mismatched checksum, or '0\n' if it is in good condition''' |
15168 | 66 filename = lfutil.findfile(repo, sha) |
67 if not filename: | |
68 return '2\n' | |
69 fd = None | |
70 try: | |
71 fd = open(filename, 'rb') | |
72 return lfutil.hexsha1(fd) == sha and '0\n' or '1\n' | |
73 finally: | |
74 if fd: | |
75 fd.close() | |
76 | |
77 def wirereposetup(ui, repo): | |
78 class lfileswirerepository(repo.__class__): | |
79 def putlfile(self, sha, fd): | |
80 # unfortunately, httprepository._callpush tries to convert its | |
81 # input file-like into a bundle before sending it, so we can't use | |
82 # it ... | |
83 if issubclass(self.__class__, httprepo.httprepository): | |
84 try: | |
85 return int(self._call('putlfile', data=fd, sha=sha, | |
86 headers={'content-type':'application/mercurial-0.1'})) | |
87 except (ValueError, urllib2.HTTPError): | |
88 return 1 | |
89 # ... but we can't use sshrepository._call because the data= | |
90 # argument won't get sent, and _callpush does exactly what we want | |
91 # in this case: send the data straight through | |
92 else: | |
93 try: | |
94 ret, output = self._callpush("putlfile", fd, sha=sha) | |
95 if ret == "": | |
96 raise error.ResponseError(_('putlfile failed:'), | |
97 output) | |
98 return int(ret) | |
99 except IOError: | |
100 return 1 | |
101 except ValueError: | |
102 raise error.ResponseError( | |
103 _('putlfile failed (unexpected response):'), ret) | |
104 | |
105 def getlfile(self, sha): | |
106 stream = self._callstream("getlfile", sha=sha) | |
107 length = stream.readline() | |
108 try: | |
109 length = int(length) | |
110 except ValueError: | |
15170
c1a4a3220711
largefiles: fix over-long lines
Matt Mackall <mpm@selenic.com>
parents:
15168
diff
changeset
|
111 self._abort(error.ResponseError(_("unexpected response:"), |
c1a4a3220711
largefiles: fix over-long lines
Matt Mackall <mpm@selenic.com>
parents:
15168
diff
changeset
|
112 length)) |
15168 | 113 return (length, stream) |
114 | |
115 def statlfile(self, sha): | |
116 try: | |
117 return int(self._call("statlfile", sha=sha)) | |
118 except (ValueError, urllib2.HTTPError): | |
15252
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
119 # If the server returns anything but an integer followed by a |
15168 | 120 # newline, newline, it's not speaking our language; if we get |
121 # an HTTP error, we can't be sure the largefile is present; | |
15252
6e809bb4f969
largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents:
15224
diff
changeset
|
122 # either way, consider it missing. |
15168 | 123 return 2 |
124 | |
125 repo.__class__ = lfileswirerepository | |
126 | |
127 # advertise the largefiles=serve capability | |
128 def capabilities(repo, proto): | |
129 return capabilities_orig(repo, proto) + ' largefiles=serve' | |
130 | |
131 # duplicate what Mercurial's new out-of-band errors mechanism does, because | |
132 # clients old and new alike both handle it well | |
133 def webproto_refuseclient(self, message): | |
134 self.req.header([('Content-Type', 'application/hg-error')]) | |
135 return message | |
136 | |
137 def sshproto_refuseclient(self, message): | |
138 self.ui.write_err('%s\n-\n' % message) | |
139 self.fout.write('\n') | |
140 self.fout.flush() | |
141 | |
142 return '' | |
143 | |
144 def heads(repo, proto): | |
145 if lfutil.islfilesrepo(repo): | |
15224
7c604d8c7e83
largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents:
15170
diff
changeset
|
146 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG) |
15168 | 147 return wireproto.heads(repo, proto) |
148 | |
149 def sshrepo_callstream(self, cmd, **args): | |
150 if cmd == 'heads' and self.capable('largefiles'): | |
151 cmd = 'lheads' | |
152 if cmd == 'batch' and self.capable('largefiles'): | |
153 args['cmds'] = args['cmds'].replace('heads ', 'lheads ') | |
154 return ssh_oldcallstream(self, cmd, **args) | |
155 | |
156 def httprepo_callstream(self, cmd, **args): | |
157 if cmd == 'heads' and self.capable('largefiles'): | |
158 cmd = 'lheads' | |
159 if cmd == 'batch' and self.capable('largefiles'): | |
160 args['cmds'] = args['cmds'].replace('heads ', 'lheads ') | |
161 return http_oldcallstream(self, cmd, **args) |