##// END OF EJS Templates
largefiles: move basestore._openstore into new module to remove cycle
liscju -
r29305:814076f4 default
parent child Browse files
Show More
@@ -8,9 +8,7
8 8
9 9 '''base class for store implementations and store-related utility code'''
10 10
11 import re
12
13 from mercurial import util, node, hg, error
11 from mercurial import util, node
14 12 from mercurial.i18n import _
15 13
16 14 import lfutil
@@ -164,63 +162,3 class basestore(object):
164 162 Returns _true_ if any problems are found!
165 163 '''
166 164 raise NotImplementedError('abstract method')
167
168 import localstore, wirestore
169
170 _storeprovider = {
171 'file': [localstore.localstore],
172 'http': [wirestore.wirestore],
173 'https': [wirestore.wirestore],
174 'ssh': [wirestore.wirestore],
175 }
176
177 _scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
178
179 # During clone this function is passed the src's ui object
180 # but it needs the dest's ui object so it can read out of
181 # the config file. Use repo.ui instead.
182 def _openstore(repo, remote=None, put=False):
183 ui = repo.ui
184
185 if not remote:
186 lfpullsource = getattr(repo, 'lfpullsource', None)
187 if lfpullsource:
188 path = ui.expandpath(lfpullsource)
189 elif put:
190 path = ui.expandpath('default-push', 'default')
191 else:
192 path = ui.expandpath('default')
193
194 # ui.expandpath() leaves 'default-push' and 'default' alone if
195 # they cannot be expanded: fallback to the empty string,
196 # meaning the current directory.
197 if path == 'default-push' or path == 'default':
198 path = ''
199 remote = repo
200 else:
201 path, _branches = hg.parseurl(path)
202 remote = hg.peer(repo, {}, path)
203
204 # The path could be a scheme so use Mercurial's normal functionality
205 # to resolve the scheme to a repository and use its path
206 path = util.safehasattr(remote, 'url') and remote.url() or remote.path
207
208 match = _scheme_re.match(path)
209 if not match: # regular filesystem path
210 scheme = 'file'
211 else:
212 scheme = match.group(1)
213
214 try:
215 storeproviders = _storeprovider[scheme]
216 except KeyError:
217 raise error.Abort(_('unsupported URL scheme %r') % scheme)
218
219 for classobj in storeproviders:
220 try:
221 return classobj(ui, repo, remote)
222 except lfutil.storeprotonotcapable:
223 pass
224
225 raise error.Abort(_('%s does not appear to be a largefile store') %
226 util.hidepassword(path))
@@ -20,7 +20,7 from hgext.convert import convcmd
20 20 from hgext.convert import filemap
21 21
22 22 import lfutil
23 import basestore
23 import storefactory
24 24
25 25 # -- Commands ----------------------------------------------------------
26 26
@@ -337,7 +337,7 def uploadlfiles(ui, rsrc, rdst, files):
337 337 if not files:
338 338 return
339 339
340 store = basestore._openstore(rsrc, rdst, put=True)
340 store = storefactory._openstore(rsrc, rdst, put=True)
341 341
342 342 at = 0
343 343 ui.debug("sending statlfile command for %d largefiles\n" % len(files))
@@ -368,7 +368,7 def verifylfiles(ui, repo, all=False, co
368 368 else:
369 369 revs = ['.']
370 370
371 store = basestore._openstore(repo)
371 store = storefactory._openstore(repo)
372 372 return store.verify(revs, contents=contents)
373 373
374 374 def cachelfiles(ui, repo, node, filelist=None):
@@ -394,7 +394,7 def cachelfiles(ui, repo, node, filelist
394 394 toget.append((lfile, expectedhash))
395 395
396 396 if toget:
397 store = basestore._openstore(repo)
397 store = storefactory._openstore(repo)
398 398 ret = store.get(toget)
399 399 return ret
400 400
@@ -17,7 +17,7 from mercurial.i18n import _
17 17
18 18 import lfutil
19 19 import lfcommands
20 import basestore
20 import storefactory
21 21
22 22 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 23
@@ -1109,7 +1109,7 def _getoutgoings(repo, other, missing,
1109 1109 lfhashes.add(lfhash)
1110 1110 lfutil.getlfilestoupload(repo, missing, dedup)
1111 1111 if lfhashes:
1112 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1112 lfexists = storefactory._openstore(repo, other).exists(lfhashes)
1113 1113 for fn, lfhash in knowns:
1114 1114 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1115 1115 addfunc(fn, lfhash)
@@ -1338,7 +1338,7 def overridecat(orig, ui, repo, file1, *
1338 1338 else:
1339 1339 hash = lfutil.readstandin(repo, lf, ctx.rev())
1340 1340 if not lfutil.inusercache(repo.ui, hash):
1341 store = basestore._openstore(repo)
1341 store = storefactory._openstore(repo)
1342 1342 success, missing = store.get([(lf, hash)])
1343 1343 if len(success) != 1:
1344 1344 raise error.Abort(
@@ -1,180 +1,23
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
5 #
6 1 # This software may be used and distributed according to the terms of the
7 2 # GNU General Public License version 2 or any later version.
8 3
9 '''base class for store implementations and store-related utility code'''
4 from __future__ import absolute_import
10 5
11 6 import re
12 7
13 from mercurial import util, node, hg, error
14 8 from mercurial.i18n import _
15 9
16 import lfutil
17
18 class StoreError(Exception):
19 '''Raised when there is a problem getting files from or putting
20 files to a central store.'''
21 def __init__(self, filename, hash, url, detail):
22 self.filename = filename
23 self.hash = hash
24 self.url = url
25 self.detail = detail
26
27 def longmessage(self):
28 return (_("error getting id %s from url %s for file %s: %s\n") %
29 (self.hash, util.hidepassword(self.url), self.filename,
30 self.detail))
31
32 def __str__(self):
33 return "%s: %s" % (util.hidepassword(self.url), self.detail)
34
35 class basestore(object):
36 def __init__(self, ui, repo, url):
37 self.ui = ui
38 self.repo = repo
39 self.url = url
40
41 def put(self, source, hash):
42 '''Put source file into the store so it can be retrieved by hash.'''
43 raise NotImplementedError('abstract method')
44
45 def exists(self, hashes):
46 '''Check to see if the store contains the given hashes. Given an
47 iterable of hashes it returns a mapping from hash to bool.'''
48 raise NotImplementedError('abstract method')
49
50 def get(self, files):
51 '''Get the specified largefiles from the store and write to local
52 files under repo.root. files is a list of (filename, hash)
53 tuples. Return (success, missing), lists of files successfully
54 downloaded and those not found in the store. success is a list
55 of (filename, hash) tuples; missing is a list of filenames that
56 we could not get. (The detailed error message will already have
57 been presented to the user, so missing is just supplied as a
58 summary.)'''
59 success = []
60 missing = []
61 ui = self.ui
62
63 at = 0
64 available = self.exists(set(hash for (_filename, hash) in files))
65 for filename, hash in files:
66 ui.progress(_('getting largefiles'), at, unit=_('files'),
67 total=len(files))
68 at += 1
69 ui.note(_('getting %s:%s\n') % (filename, hash))
70
71 if not available.get(hash):
72 ui.warn(_('%s: largefile %s not available from %s\n')
73 % (filename, hash, util.hidepassword(self.url)))
74 missing.append(filename)
75 continue
76
77 if self._gethash(filename, hash):
78 success.append((filename, hash))
79 else:
80 missing.append(filename)
81
82 ui.progress(_('getting largefiles'), None)
83 return (success, missing)
84
85 def _gethash(self, filename, hash):
86 """Get file with the provided hash and store it in the local repo's
87 store and in the usercache.
88 filename is for informational messages only.
89 """
90 util.makedirs(lfutil.storepath(self.repo, ''))
91 storefilename = lfutil.storepath(self.repo, hash)
92
93 tmpname = storefilename + '.tmp'
94 tmpfile = util.atomictempfile(tmpname,
95 createmode=self.repo.store.createmode)
10 from mercurial import (
11 error,
12 hg,
13 util,
14 )
96 15
97 try:
98 gothash = self._getfile(tmpfile, filename, hash)
99 except StoreError as err:
100 self.ui.warn(err.longmessage())
101 gothash = ""
102 tmpfile.close()
103
104 if gothash != hash:
105 if gothash != "":
106 self.ui.warn(_('%s: data corruption (expected %s, got %s)\n')
107 % (filename, hash, gothash))
108 util.unlink(tmpname)
109 return False
110
111 util.rename(tmpname, storefilename)
112 lfutil.linktousercache(self.repo, hash)
113 return True
114
115 def verify(self, revs, contents=False):
116 '''Verify the existence (and, optionally, contents) of every big
117 file revision referenced by every changeset in revs.
118 Return 0 if all is well, non-zero on any errors.'''
119
120 self.ui.status(_('searching %d changesets for largefiles\n') %
121 len(revs))
122 verified = set() # set of (filename, filenode) tuples
123 filestocheck = [] # list of (cset, filename, expectedhash)
124 for rev in revs:
125 cctx = self.repo[rev]
126 cset = "%d:%s" % (cctx.rev(), node.short(cctx.node()))
127
128 for standin in cctx:
129 filename = lfutil.splitstandin(standin)
130 if filename:
131 fctx = cctx[standin]
132 key = (filename, fctx.filenode())
133 if key not in verified:
134 verified.add(key)
135 expectedhash = fctx.data()[0:40]
136 filestocheck.append((cset, filename, expectedhash))
137
138 failed = self._verifyfiles(contents, filestocheck)
139
140 numrevs = len(verified)
141 numlfiles = len(set([fname for (fname, fnode) in verified]))
142 if contents:
143 self.ui.status(
144 _('verified contents of %d revisions of %d largefiles\n')
145 % (numrevs, numlfiles))
146 else:
147 self.ui.status(
148 _('verified existence of %d revisions of %d largefiles\n')
149 % (numrevs, numlfiles))
150 return int(failed)
151
152 def _getfile(self, tmpfile, filename, hash):
153 '''Fetch one revision of one file from the store and write it
154 to tmpfile. Compute the hash of the file on-the-fly as it
155 downloads and return the hash. Close tmpfile. Raise
156 StoreError if unable to download the file (e.g. it does not
157 exist in the store).'''
158 raise NotImplementedError('abstract method')
159
160 def _verifyfiles(self, contents, filestocheck):
161 '''Perform the actual verification of files in the store.
162 'contents' controls verification of content hash.
163 'filestocheck' is list of files to check.
164 Returns _true_ if any problems are found!
165 '''
166 raise NotImplementedError('abstract method')
167
168 import localstore, wirestore
169
170 _storeprovider = {
171 'file': [localstore.localstore],
172 'http': [wirestore.wirestore],
173 'https': [wirestore.wirestore],
174 'ssh': [wirestore.wirestore],
175 }
176
177 _scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
16 from . import (
17 lfutil,
18 localstore,
19 wirestore,
20 )
178 21
179 22 # During clone this function is passed the src's ui object
180 23 # but it needs the dest's ui object so it can read out of
@@ -224,3 +67,12 def _openstore(repo, remote=None, put=Fa
224 67
225 68 raise error.Abort(_('%s does not appear to be a largefile store') %
226 69 util.hidepassword(path))
70
71 _storeprovider = {
72 'file': [localstore.localstore],
73 'http': [wirestore.wirestore],
74 'https': [wirestore.wirestore],
75 'ssh': [wirestore.wirestore],
76 }
77
78 _scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
@@ -179,5 +179,3 outputs, which should be fixed later.
179 179 > -X tests/test-hgweb-no-request-uri.t \
180 180 > -X tests/test-hgweb-non-interactive.t \
181 181 > | sed 's-\\-/-g' | python "$import_checker" -
182 Import cycle: hgext.largefiles.basestore -> hgext.largefiles.localstore -> hgext.largefiles.basestore
183 [1]
General Comments 0
You need to be logged in to leave comments. Login now