##// END OF EJS Templates
introduce store classes...
Adrian Buehlmann -
r6840:80e51429 default
parent child Browse files
Show More
@@ -60,30 +60,13 b' class localrepository(repo.repository):'
60 if r not in self.supported:
60 if r not in self.supported:
61 raise repo.RepoError(_("requirement '%s' not supported") % r)
61 raise repo.RepoError(_("requirement '%s' not supported") % r)
62
62
63 # setup store
63 self.store = store.store(requirements, self.path)
64 if "store" in requirements:
65 self.encodefn = store.encodefilename
66 self.decodefn = store.decodefilename
67 self.spath = os.path.join(self.path, "store")
68 else:
69 self.encodefn = lambda x: x
70 self.decodefn = lambda x: x
71 self.spath = self.path
72
64
73 try:
65 self.spath = self.store.path
74 # files in .hg/ will be created using this mode
66 self.sopener = self.store.opener
75 mode = os.stat(self.spath).st_mode
67 self.sjoin = self.store.join
76 # avoid some useless chmods
68 self._createmode = self.store.createmode
77 if (0777 & ~util._umask) == (0777 & mode):
69 self.opener.createmode = self.store.createmode
78 mode = None
79 except OSError:
80 mode = None
81
82 self._createmode = mode
83 self.opener.createmode = mode
84 sopener = util.opener(self.spath)
85 sopener.createmode = mode
86 self.sopener = store.encodedopener(sopener, self.encodefn)
87
70
88 self.ui = ui.ui(parentui=parentui)
71 self.ui = ui.ui(parentui=parentui)
89 try:
72 try:
@@ -481,10 +464,6 b' class localrepository(repo.repository):'
481 def join(self, f):
464 def join(self, f):
482 return os.path.join(self.path, f)
465 return os.path.join(self.path, f)
483
466
484 def sjoin(self, f):
485 f = self.encodefn(f)
486 return os.path.join(self.spath, f)
487
488 def wjoin(self, f):
467 def wjoin(self, f):
489 return os.path.join(self.root, f)
468 return os.path.join(self.root, f)
490
469
@@ -2061,6 +2040,25 b' class localrepository(repo.repository):'
2061 return self.stream_in(remote)
2040 return self.stream_in(remote)
2062 return self.pull(remote, heads)
2041 return self.pull(remote, heads)
2063
2042
2043 def storefiles(self):
2044 '''get all *.i and *.d files in the store
2045
2046 Returns (list of (filename, size), total_bytes)'''
2047
2048 lock = None
2049 try:
2050 self.ui.debug('scanning\n')
2051 entries = []
2052 total_bytes = 0
2053 # get consistent snapshot of repo, lock during scan
2054 lock = self.lock()
2055 for name, size in self.store.walk():
2056 entries.append((name, size))
2057 total_bytes += size
2058 return entries, total_bytes
2059 finally:
2060 del lock
2061
2064 # used to avoid circular references so destructors work
2062 # used to avoid circular references so destructors work
2065 def aftertrans(files):
2063 def aftertrans(files):
2066 renamefiles = [tuple(t) for t in files]
2064 renamefiles = [tuple(t) for t in files]
@@ -55,14 +55,13 b' class statichttprepository(localrepo.loc'
55
55
56 # setup store
56 # setup store
57 if "store" in requirements:
57 if "store" in requirements:
58 self.encodefn = store.encodefilename
59 self.decodefn = store.decodefilename
60 self.spath = self.path + "/store"
58 self.spath = self.path + "/store"
61 else:
59 else:
62 self.encodefn = lambda x: x
63 self.decodefn = lambda x: x
64 self.spath = self.path
60 self.spath = self.path
65 self.sopener = store.encodedopener(opener(self.spath), self.encodefn)
61 self.encodefn = store.encodefn(requirements)
62 so = opener(self.spath)
63 self.sopener = lambda path, *args, **kw: so(
64 self.encodefn(path), *args, **kw)
66
65
67 self.manifest = manifest.manifest(self.sopener)
66 self.manifest = manifest.manifest(self.sopener)
68 self.changelog = changelog.changelog(self.sopener)
67 self.changelog = changelog.changelog(self.sopener)
@@ -5,6 +5,8 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, stat, osutil, util
9
8 def _buildencodefun():
10 def _buildencodefun():
9 e = '_'
11 e = '_'
10 win_reserved = [ord(x) for x in '\\:*?"<>|']
12 win_reserved = [ord(x) for x in '\\:*?"<>|']
@@ -33,7 +35,91 b' def _buildencodefun():'
33
35
34 encodefilename, decodefilename = _buildencodefun()
36 encodefilename, decodefilename = _buildencodefun()
35
37
36 def encodedopener(openerfn, fn):
38 def _dirwalk(path, recurse):
37 def o(path, *args, **kw):
39 '''yields (filename, size)'''
38 return openerfn(fn(path), *args, **kw)
40 for e, kind, st in osutil.listdir(path, stat=True):
39 return o
41 pe = os.path.join(path, e)
42 if kind == stat.S_IFDIR:
43 if recurse:
44 for x in _dirwalk(pe, True):
45 yield x
46 elif kind == stat.S_IFREG:
47 yield pe, st.st_size
48
49 class _store:
50 '''base class for local repository stores'''
51 def __init__(self, path):
52 self.path = path
53 try:
54 # files in .hg/ will be created using this mode
55 mode = os.stat(self.path).st_mode
56 # avoid some useless chmods
57 if (0777 & ~util._umask) == (0777 & mode):
58 mode = None
59 except OSError:
60 mode = None
61 self.createmode = mode
62
63 def join(self, f):
64 return os.path.join(self.path, f)
65
66 def _revlogfiles(self, relpath='', recurse=False):
67 '''yields (filename, size)'''
68 if relpath:
69 path = os.path.join(self.path, relpath)
70 else:
71 path = self.path
72 striplen = len(self.path) + len(os.sep)
73 filetypes = ('.d', '.i')
74 for f, size in _dirwalk(path, recurse):
75 if (len(f) > 2) and f[-2:] in filetypes:
76 yield util.pconvert(f[striplen:]), size
77
78 def _datafiles(self):
79 for x in self._revlogfiles('data', True):
80 yield x
81
82 def walk(self):
83 '''yields (direncoded filename, size)'''
84 # yield data files first
85 for x in self._datafiles():
86 yield x
87 # yield manifest before changelog
88 meta = util.sort(self._revlogfiles())
89 meta.reverse()
90 for x in meta:
91 yield x
92
93 class directstore(_store):
94 def __init__(self, path):
95 _store.__init__(self, path)
96 self.encodefn = lambda x: x
97 self.opener = util.opener(self.path)
98 self.opener.createmode = self.createmode
99
100 class encodedstore(_store):
101 def __init__(self, path):
102 _store.__init__(self, os.path.join(path, 'store'))
103 self.encodefn = encodefilename
104 op = util.opener(self.path)
105 op.createmode = self.createmode
106 self.opener = lambda f, *args, **kw: op(self.encodefn(f), *args, **kw)
107
108 def _datafiles(self):
109 for f, size in self._revlogfiles('data', True):
110 yield decodefilename(f), size
111
112 def join(self, f):
113 return os.path.join(self.path, self.encodefn(f))
114
115 def encodefn(requirements):
116 if 'store' not in requirements:
117 return lambda x: x
118 else:
119 return encodefilename
120
121 def store(requirements, path):
122 if 'store' not in requirements:
123 return directstore(path)
124 else:
125 return encodedstore(path)
@@ -5,40 +5,12 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, osutil, stat, util, lock
8 import util, lock
9
9
10 # if server supports streaming clone, it advertises "stream"
10 # if server supports streaming clone, it advertises "stream"
11 # capability with value that is version+flags of repo it is serving.
11 # capability with value that is version+flags of repo it is serving.
12 # client only streams if it can read that repo format.
12 # client only streams if it can read that repo format.
13
13
14 def walkrepo(root):
15 '''iterate over metadata files in repository.
16 walk in natural (sorted) order.
17 yields 2-tuples: name of .d or .i file, size of file.'''
18
19 strip_count = len(root) + len(os.sep)
20 def walk(path, recurse):
21 for e, kind, st in osutil.listdir(path, stat=True):
22 pe = os.path.join(path, e)
23 if kind == stat.S_IFDIR:
24 if recurse:
25 for x in walk(pe, True):
26 yield x
27 else:
28 if kind != stat.S_IFREG or len(e) < 2:
29 continue
30 sfx = e[-2:]
31 if sfx in ('.d', '.i'):
32 yield pe[strip_count:], st.st_size
33 # write file data first
34 for x in walk(os.path.join(root, 'data'), True):
35 yield x
36 # write manifest before changelog
37 meta = util.sort(walk(root, False))
38 meta.reverse()
39 for x in meta:
40 yield x
41
42 # stream file format is simple.
14 # stream file format is simple.
43 #
15 #
44 # server writes out line that says how many files, how many total
16 # server writes out line that says how many files, how many total
@@ -59,28 +31,14 b' def stream_out(repo, fileobj, untrusted='
59 fileobj.write('1\n')
31 fileobj.write('1\n')
60 return
32 return
61
33
62 # get consistent snapshot of repo. lock during scan so lock not
63 # needed while we stream, and commits can happen.
64 repolock = None
65 try:
34 try:
66 try:
35 entries, total_bytes = repo.storefiles()
67 repolock = repo.lock()
36 except (lock.LockHeld, lock.LockUnavailable), inst:
68 except (lock.LockHeld, lock.LockUnavailable), inst:
37 repo.ui.warn('locking the repository failed: %s\n' % (inst,))
69 repo.ui.warn('locking the repository failed: %s\n' % (inst,))
38 fileobj.write('2\n')
70 fileobj.write('2\n')
39 return
71 return
72
40
73 fileobj.write('0\n')
41 fileobj.write('0\n')
74 repo.ui.debug('scanning\n')
75 entries = []
76 total_bytes = 0
77 for name, size in walkrepo(repo.spath):
78 name = repo.decodefn(util.pconvert(name))
79 entries.append((name, size))
80 total_bytes += size
81 finally:
82 del repolock
83
84 repo.ui.debug('%d files, %d bytes to transfer\n' %
42 repo.ui.debug('%d files, %d bytes to transfer\n' %
85 (len(entries), total_bytes))
43 (len(entries), total_bytes))
86 fileobj.write('%d %d\n' % (len(entries), total_bytes))
44 fileobj.write('%d %d\n' % (len(entries), total_bytes))
General Comments 0
You need to be logged in to leave comments. Login now