##// END OF EJS Templates
largefiles: import whole modules instead of importing parts of them...
Mads Kiilerich -
r21084:70252bdf default
parent child Browse files
Show More
@@ -1,177 +1,176
1 1 # Copyright 2011 Fog Creek Software
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 import os
7 7 import urllib2
8 8 import re
9 9
10 10 from mercurial import error, httppeer, util, wireproto
11 from mercurial.wireproto import batchable, future
12 11 from mercurial.i18n import _
13 12
14 13 import lfutil
15 14
16 15 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
17 16 '\n\nPlease enable it in your Mercurial config '
18 17 'file.\n')
19 18
20 19 # these will all be replaced by largefiles.uisetup
21 20 capabilitiesorig = None
22 21 ssholdcallstream = None
23 22 httpoldcallstream = None
24 23
25 24 def putlfile(repo, proto, sha):
26 25 '''Put a largefile into a repository's local store and into the
27 26 user cache.'''
28 27 proto.redirect()
29 28
30 29 path = lfutil.storepath(repo, sha)
31 30 util.makedirs(os.path.dirname(path))
32 31 tmpfp = util.atomictempfile(path, createmode=repo.store.createmode)
33 32
34 33 try:
35 34 try:
36 35 proto.getfile(tmpfp)
37 36 tmpfp._fp.seek(0)
38 37 if sha != lfutil.hexsha1(tmpfp._fp):
39 38 raise IOError(0, _('largefile contents do not match hash'))
40 39 tmpfp.close()
41 40 lfutil.linktousercache(repo, sha)
42 41 except IOError, e:
43 42 repo.ui.warn(_('largefiles: failed to put %s into store: %s\n') %
44 43 (sha, e.strerror))
45 44 return wireproto.pushres(1)
46 45 finally:
47 46 tmpfp.discard()
48 47
49 48 return wireproto.pushres(0)
50 49
51 50 def getlfile(repo, proto, sha):
52 51 '''Retrieve a largefile from the repository-local cache or system
53 52 cache.'''
54 53 filename = lfutil.findfile(repo, sha)
55 54 if not filename:
56 55 raise util.Abort(_('requested largefile %s not present in cache') % sha)
57 56 f = open(filename, 'rb')
58 57 length = os.fstat(f.fileno())[6]
59 58
60 59 # Since we can't set an HTTP content-length header here, and
61 60 # Mercurial core provides no way to give the length of a streamres
62 61 # (and reading the entire file into RAM would be ill-advised), we
63 62 # just send the length on the first line of the response, like the
64 63 # ssh proto does for string responses.
65 64 def generator():
66 65 yield '%d\n' % length
67 66 for chunk in util.filechunkiter(f):
68 67 yield chunk
69 68 return wireproto.streamres(generator())
70 69
71 70 def statlfile(repo, proto, sha):
72 71 '''Return '2\n' if the largefile is missing, '0\n' if it seems to be in
73 72 good condition.
74 73
75 74 The value 1 is reserved for mismatched checksum, but that is too expensive
76 75 to be verified on every stat and must be caught be running 'hg verify'
77 76 server side.'''
78 77 filename = lfutil.findfile(repo, sha)
79 78 if not filename:
80 79 return '2\n'
81 80 return '0\n'
82 81
83 82 def wirereposetup(ui, repo):
84 83 class lfileswirerepository(repo.__class__):
85 84 def putlfile(self, sha, fd):
86 85 # unfortunately, httprepository._callpush tries to convert its
87 86 # input file-like into a bundle before sending it, so we can't use
88 87 # it ...
89 88 if issubclass(self.__class__, httppeer.httppeer):
90 89 res = None
91 90 try:
92 91 res = self._call('putlfile', data=fd, sha=sha,
93 92 headers={'content-type':'application/mercurial-0.1'})
94 93 d, output = res.split('\n', 1)
95 94 for l in output.splitlines(True):
96 95 self.ui.warn(_('remote: '), l) # assume l ends with \n
97 96 return int(d)
98 97 except (ValueError, urllib2.HTTPError):
99 98 self.ui.warn(_('unexpected putlfile response: %r\n') % res)
100 99 return 1
101 100 # ... but we can't use sshrepository._call because the data=
102 101 # argument won't get sent, and _callpush does exactly what we want
103 102 # in this case: send the data straight through
104 103 else:
105 104 try:
106 105 ret, output = self._callpush("putlfile", fd, sha=sha)
107 106 if ret == "":
108 107 raise error.ResponseError(_('putlfile failed:'),
109 108 output)
110 109 return int(ret)
111 110 except IOError:
112 111 return 1
113 112 except ValueError:
114 113 raise error.ResponseError(
115 114 _('putlfile failed (unexpected response):'), ret)
116 115
117 116 def getlfile(self, sha):
118 117 """returns an iterable with the chunks of the file with sha sha"""
119 118 stream = self._callstream("getlfile", sha=sha)
120 119 length = stream.readline()
121 120 try:
122 121 length = int(length)
123 122 except ValueError:
124 123 self._abort(error.ResponseError(_("unexpected response:"),
125 124 length))
126 125
127 126 # SSH streams will block if reading more than length
128 127 for chunk in util.filechunkiter(stream, 128 * 1024, length):
129 128 yield chunk
130 129 # HTTP streams must hit the end to process the last empty
131 130 # chunk of Chunked-Encoding so the connection can be reused.
132 131 if issubclass(self.__class__, httppeer.httppeer):
133 132 chunk = stream.read(1)
134 133 if chunk:
135 134 self._abort(error.ResponseError(_("unexpected response:"),
136 135 chunk))
137 136
138 @batchable
137 @wireproto.batchable
139 138 def statlfile(self, sha):
140 f = future()
139 f = wireproto.future()
141 140 result = {'sha': sha}
142 141 yield result, f
143 142 try:
144 143 yield int(f.value)
145 144 except (ValueError, urllib2.HTTPError):
146 145 # If the server returns anything but an integer followed by a
147 146 # newline, newline, it's not speaking our language; if we get
148 147 # an HTTP error, we can't be sure the largefile is present;
149 148 # either way, consider it missing.
150 149 yield 2
151 150
152 151 repo.__class__ = lfileswirerepository
153 152
154 153 # advertise the largefiles=serve capability
155 154 def capabilities(repo, proto):
156 155 return capabilitiesorig(repo, proto) + ' largefiles=serve'
157 156
158 157 def heads(repo, proto):
159 158 if lfutil.islfilesrepo(repo):
160 159 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
161 160 return wireproto.heads(repo, proto)
162 161
163 162 def sshrepocallstream(self, cmd, **args):
164 163 if cmd == 'heads' and self.capable('largefiles'):
165 164 cmd = 'lheads'
166 165 if cmd == 'batch' and self.capable('largefiles'):
167 166 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
168 167 return ssholdcallstream(self, cmd, **args)
169 168
170 169 headsre = re.compile(r'(^|;)heads\b')
171 170
172 171 def httprepocallstream(self, cmd, **args):
173 172 if cmd == 'heads' and self.capable('largefiles'):
174 173 cmd = 'lheads'
175 174 if cmd == 'batch' and self.capable('largefiles'):
176 175 args['cmds'] = headsre.sub('lheads', args['cmds'])
177 176 return httpoldcallstream(self, cmd, **args)
@@ -1,100 +1,99
1 1 # Copyright 2010-2011 Fog Creek Software
2 2 # Copyright 2010-2011 Unity Technologies
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 '''remote largefile store; the base class for wirestore'''
8 8
9 9 import urllib2
10 10
11 from mercurial import util
11 from mercurial import util, wireproto
12 12 from mercurial.i18n import _
13 from mercurial.wireproto import remotebatch
14 13
15 14 import lfutil
16 15 import basestore
17 16
18 17 class remotestore(basestore.basestore):
19 18 '''a largefile store accessed over a network'''
20 19 def __init__(self, ui, repo, url):
21 20 super(remotestore, self).__init__(ui, repo, url)
22 21
23 22 def put(self, source, hash):
24 23 if self.sendfile(source, hash):
25 24 raise util.Abort(
26 25 _('remotestore: could not put %s to remote store %s')
27 26 % (source, util.hidepassword(self.url)))
28 27 self.ui.debug(
29 28 _('remotestore: put %s to remote store %s\n')
30 29 % (source, util.hidepassword(self.url)))
31 30
32 31 def exists(self, hashes):
33 32 return dict((h, s == 0) for (h, s) in # dict-from-generator
34 33 self._stat(hashes).iteritems())
35 34
36 35 def sendfile(self, filename, hash):
37 36 self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash))
38 37 fd = None
39 38 try:
40 39 try:
41 40 fd = lfutil.httpsendfile(self.ui, filename)
42 41 except IOError, e:
43 42 raise util.Abort(
44 43 _('remotestore: could not open file %s: %s')
45 44 % (filename, str(e)))
46 45 return self._put(hash, fd)
47 46 finally:
48 47 if fd:
49 48 fd.close()
50 49
51 50 def _getfile(self, tmpfile, filename, hash):
52 51 try:
53 52 chunks = self._get(hash)
54 53 except urllib2.HTTPError, e:
55 54 # 401s get converted to util.Aborts; everything else is fine being
56 55 # turned into a StoreError
57 56 raise basestore.StoreError(filename, hash, self.url, str(e))
58 57 except urllib2.URLError, e:
59 58 # This usually indicates a connection problem, so don't
60 59 # keep trying with the other files... they will probably
61 60 # all fail too.
62 61 raise util.Abort('%s: %s' %
63 62 (util.hidepassword(self.url), e.reason))
64 63 except IOError, e:
65 64 raise basestore.StoreError(filename, hash, self.url, str(e))
66 65
67 66 return lfutil.copyandhash(chunks, tmpfile)
68 67
69 68 def _verifyfile(self, cctx, cset, contents, standin, verified):
70 69 filename = lfutil.splitstandin(standin)
71 70 if not filename:
72 71 return False
73 72 fctx = cctx[standin]
74 73 key = (filename, fctx.filenode())
75 74 if key in verified:
76 75 return False
77 76
78 77 verified.add(key)
79 78
80 79 expecthash = fctx.data()[0:40]
81 80 stat = self._stat([expecthash])[expecthash]
82 81 if not stat:
83 82 return False
84 83 elif stat == 1:
85 84 self.ui.warn(
86 85 _('changeset %s: %s: contents differ\n')
87 86 % (cset, filename))
88 87 return True # failed
89 88 elif stat == 2:
90 89 self.ui.warn(
91 90 _('changeset %s: %s missing\n')
92 91 % (cset, filename))
93 92 return True # failed
94 93 else:
95 94 raise RuntimeError('verify failed: unexpected response from '
96 95 'statlfile (%r)' % stat)
97 96
98 97 def batch(self):
99 98 '''Support for remote batching.'''
100 return remotebatch(self)
99 return wireproto.remotebatch(self)
@@ -1,177 +1,177
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''setup for largefiles extension: uisetup'''
10 10
11 11 from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
12 httppeer, merge, scmutil, sshpeer, wireproto, revset
12 httppeer, merge, scmutil, sshpeer, wireproto, revset, subrepo
13 13 from mercurial.i18n import _
14 14 from mercurial.hgweb import hgweb_mod, webcommands
15 from mercurial.subrepo import hgsubrepo
16 15
17 16 import overrides
18 17 import proto
19 18
20 19 def uisetup(ui):
21 20 # Disable auto-status for some commands which assume that all
22 21 # files in the result are under Mercurial's control
23 22
24 23 entry = extensions.wrapcommand(commands.table, 'add',
25 24 overrides.overrideadd)
26 25 addopt = [('', 'large', None, _('add as largefile')),
27 26 ('', 'normal', None, _('add as normal file')),
28 27 ('', 'lfsize', '', _('add all files above this size '
29 28 '(in megabytes) as largefiles '
30 29 '(default: 10)'))]
31 30 entry[1].extend(addopt)
32 31
33 32 # The scmutil function is called both by the (trivial) addremove command,
34 33 # and in the process of handling commit -A (issue3542)
35 34 entry = extensions.wrapfunction(scmutil, 'addremove',
36 35 overrides.scmutiladdremove)
37 36 entry = extensions.wrapcommand(commands.table, 'remove',
38 37 overrides.overrideremove)
39 38 entry = extensions.wrapcommand(commands.table, 'forget',
40 39 overrides.overrideforget)
41 40
42 41 # Subrepos call status function
43 42 entry = extensions.wrapcommand(commands.table, 'status',
44 43 overrides.overridestatus)
45 entry = extensions.wrapfunction(hgsubrepo, 'status',
44 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
46 45 overrides.overridestatusfn)
47 46
48 47 entry = extensions.wrapcommand(commands.table, 'log',
49 48 overrides.overridelog)
50 49 entry = extensions.wrapcommand(commands.table, 'rollback',
51 50 overrides.overriderollback)
52 51 entry = extensions.wrapcommand(commands.table, 'verify',
53 52 overrides.overrideverify)
54 53
55 54 verifyopt = [('', 'large', None,
56 55 _('verify that all largefiles in current revision exists')),
57 56 ('', 'lfa', None,
58 57 _('verify largefiles in all revisions, not just current')),
59 58 ('', 'lfc', None,
60 59 _('verify local largefile contents, not just existence'))]
61 60 entry[1].extend(verifyopt)
62 61
63 62 entry = extensions.wrapcommand(commands.table, 'debugstate',
64 63 overrides.overridedebugstate)
65 64 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
66 65 entry[1].extend(debugstateopt)
67 66
68 67 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
69 68 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
70 69 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
71 70 entry[1].extend(outgoingopt)
72 71 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
73 72 entry = extensions.wrapcommand(commands.table, 'summary',
74 73 overrides.overridesummary)
75 74 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
76 75 entry[1].extend(summaryopt)
77 76 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
78 77
79 78 entry = extensions.wrapcommand(commands.table, 'update',
80 79 overrides.overrideupdate)
81 80 entry = extensions.wrapcommand(commands.table, 'pull',
82 81 overrides.overridepull)
83 82 pullopt = [('', 'all-largefiles', None,
84 83 _('download all pulled versions of largefiles (DEPRECATED)')),
85 84 ('', 'lfrev', [],
86 85 _('download largefiles for these revisions'), _('REV'))]
87 86 entry[1].extend(pullopt)
88 87 revset.symbols['pulled'] = overrides.pulledrevsetsymbol
89 88
90 89 entry = extensions.wrapcommand(commands.table, 'clone',
91 90 overrides.overrideclone)
92 91 cloneopt = [('', 'all-largefiles', None,
93 92 _('download all versions of all largefiles'))]
94 93 entry[1].extend(cloneopt)
95 94 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
96 95
97 96 entry = extensions.wrapcommand(commands.table, 'cat',
98 97 overrides.overridecat)
99 98 entry = extensions.wrapfunction(merge, '_checkunknownfile',
100 99 overrides.overridecheckunknownfile)
101 100 entry = extensions.wrapfunction(merge, 'calculateupdates',
102 101 overrides.overridecalculateupdates)
103 102 entry = extensions.wrapfunction(filemerge, 'filemerge',
104 103 overrides.overridefilemerge)
105 104 entry = extensions.wrapfunction(cmdutil, 'copy',
106 105 overrides.overridecopy)
107 106
108 107 # Summary calls dirty on the subrepos
109 entry = extensions.wrapfunction(hgsubrepo, 'dirty',
108 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
110 109 overrides.overridedirty)
111 110
112 111 # Backout calls revert so we need to override both the command and the
113 112 # function
114 113 entry = extensions.wrapcommand(commands.table, 'revert',
115 114 overrides.overriderevert)
116 115 entry = extensions.wrapfunction(commands, 'revert',
117 116 overrides.overriderevert)
118 117
119 118 extensions.wrapfunction(hg, 'updaterepo', overrides.hgupdaterepo)
120 119 extensions.wrapfunction(hg, 'merge', overrides.hgmerge)
121 120
122 121 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
123 extensions.wrapfunction(hgsubrepo, 'archive', overrides.hgsubrepoarchive)
122 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
123 overrides.hgsubrepoarchive)
124 124 extensions.wrapfunction(cmdutil, 'bailifchanged',
125 125 overrides.overridebailifchanged)
126 126
127 127 # create the new wireproto commands ...
128 128 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
129 129 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
130 130 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
131 131
132 132 # ... and wrap some existing ones
133 133 wireproto.commands['capabilities'] = (proto.capabilities, '')
134 134 wireproto.commands['heads'] = (proto.heads, '')
135 135 wireproto.commands['lheads'] = (wireproto.heads, '')
136 136
137 137 # make putlfile behave the same as push and {get,stat}lfile behave
138 138 # the same as pull w.r.t. permissions checks
139 139 hgweb_mod.perms['putlfile'] = 'push'
140 140 hgweb_mod.perms['getlfile'] = 'pull'
141 141 hgweb_mod.perms['statlfile'] = 'pull'
142 142
143 143 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
144 144
145 145 # the hello wireproto command uses wireproto.capabilities, so it won't see
146 146 # our largefiles capability unless we replace the actual function as well.
147 147 proto.capabilitiesorig = wireproto.capabilities
148 148 wireproto.capabilities = proto.capabilities
149 149
150 150 # can't do this in reposetup because it needs to have happened before
151 151 # wirerepo.__init__ is called
152 152 proto.ssholdcallstream = sshpeer.sshpeer._callstream
153 153 proto.httpoldcallstream = httppeer.httppeer._callstream
154 154 sshpeer.sshpeer._callstream = proto.sshrepocallstream
155 155 httppeer.httppeer._callstream = proto.httprepocallstream
156 156
157 157 # override some extensions' stuff as well
158 158 for name, module in extensions.extensions():
159 159 if name == 'fetch':
160 160 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
161 161 overrides.overridefetch)
162 162 if name == 'purge':
163 163 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
164 164 overrides.overridepurge)
165 165 if name == 'rebase':
166 166 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
167 167 overrides.overriderebase)
168 168 if name == 'transplant':
169 169 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
170 170 overrides.overridetransplant)
171 171 if name == 'convert':
172 172 convcmd = getattr(module, 'convcmd')
173 173 hgsink = getattr(convcmd, 'mercurial_sink')
174 174 extensions.wrapfunction(hgsink, 'before',
175 175 overrides.mercurialsinkbefore)
176 176 extensions.wrapfunction(hgsink, 'after',
177 177 overrides.mercurialsinkafter)
General Comments 0
You need to be logged in to leave comments. Login now