Show More
@@ -10,21 +10,7 b' import revlog' | |||||
10 | class filelog(revlog.revlog): |
|
10 | class filelog(revlog.revlog): | |
11 | def __init__(self, opener, path): |
|
11 | def __init__(self, opener, path): | |
12 | revlog.revlog.__init__(self, opener, |
|
12 | revlog.revlog.__init__(self, opener, | |
13 |
"/".join(("data", |
|
13 | "/".join(("data", path + ".i"))) | |
14 |
|
||||
15 | # This avoids a collision between a file named foo and a dir named |
|
|||
16 | # foo.i or foo.d |
|
|||
17 | def encodedir(self, path): |
|
|||
18 | return (path |
|
|||
19 | .replace(".hg/", ".hg.hg/") |
|
|||
20 | .replace(".i/", ".i.hg/") |
|
|||
21 | .replace(".d/", ".d.hg/")) |
|
|||
22 |
|
||||
23 | def decodedir(self, path): |
|
|||
24 | return (path |
|
|||
25 | .replace(".d.hg/", ".d/") |
|
|||
26 | .replace(".i.hg/", ".i/") |
|
|||
27 | .replace(".hg.hg/", ".hg/")) |
|
|||
28 |
|
14 | |||
29 | def read(self, node): |
|
15 | def read(self, node): | |
30 | t = self.revision(node) |
|
16 | t = self.revision(node) |
@@ -2026,7 +2026,8 b' class localrepository(repo.repository):' | |||||
2026 | raise error.ResponseError( |
|
2026 | raise error.ResponseError( | |
2027 | _('Unexpected response from remote server:'), l) |
|
2027 | _('Unexpected response from remote server:'), l) | |
2028 | self.ui.debug(_('adding %s (%s)\n') % (name, util.bytecount(size))) |
|
2028 | self.ui.debug(_('adding %s (%s)\n') % (name, util.bytecount(size))) | |
2029 | ofp = self.sopener(name, 'w') |
|
2029 | # for backwards compat, name was partially encoded | |
|
2030 | ofp = self.sopener(store.decodedir(name), 'w') | |||
2030 | for chunk in util.filechunkiter(fp, limit=size): |
|
2031 | for chunk in util.filechunkiter(fp, limit=size): | |
2031 | ofp.write(chunk) |
|
2032 | ofp.write(chunk) | |
2032 | ofp.close() |
|
2033 | ofp.close() |
@@ -11,6 +11,24 b' import os, stat' | |||||
11 |
|
11 | |||
12 | _sha = util.sha1 |
|
12 | _sha = util.sha1 | |
13 |
|
13 | |||
|
14 | # This avoids a collision between a file named foo and a dir named | |||
|
15 | # foo.i or foo.d | |||
|
16 | def encodedir(path): | |||
|
17 | if not path.startswith('data/'): | |||
|
18 | return path | |||
|
19 | return (path | |||
|
20 | .replace(".hg/", ".hg.hg/") | |||
|
21 | .replace(".i/", ".i.hg/") | |||
|
22 | .replace(".d/", ".d.hg/")) | |||
|
23 | ||||
|
24 | def decodedir(path): | |||
|
25 | if not path.startswith('data/'): | |||
|
26 | return path | |||
|
27 | return (path | |||
|
28 | .replace(".d.hg/", ".d/") | |||
|
29 | .replace(".i.hg/", ".i/") | |||
|
30 | .replace(".hg.hg/", ".hg/")) | |||
|
31 | ||||
14 | def _buildencodefun(): |
|
32 | def _buildencodefun(): | |
15 | e = '_' |
|
33 | e = '_' | |
16 | win_reserved = [ord(x) for x in '\\:*?"<>|'] |
|
34 | win_reserved = [ord(x) for x in '\\:*?"<>|'] | |
@@ -34,8 +52,8 b' def _buildencodefun():' | |||||
34 | pass |
|
52 | pass | |
35 | else: |
|
53 | else: | |
36 | raise KeyError |
|
54 | raise KeyError | |
37 | return (lambda s: "".join([cmap[c] for c in s]), |
|
55 | return (lambda s: "".join([cmap[c] for c in encodedir(s)]), | |
38 | lambda s: "".join(list(decode(s)))) |
|
56 | lambda s: decodedir("".join(list(decode(s))))) | |
39 |
|
57 | |||
40 | encodefilename, decodefilename = _buildencodefun() |
|
58 | encodefilename, decodefilename = _buildencodefun() | |
41 |
|
59 | |||
@@ -104,6 +122,8 b' def hybridencode(path):' | |||||
104 | ''' |
|
122 | ''' | |
105 | if not path.startswith('data/'): |
|
123 | if not path.startswith('data/'): | |
106 | return path |
|
124 | return path | |
|
125 | # escape directories ending with .i and .d | |||
|
126 | path = encodedir(path) | |||
107 | ndpath = path[len('data/'):] |
|
127 | ndpath = path[len('data/'):] | |
108 | res = 'data/' + auxencode(encodefilename(ndpath)) |
|
128 | res = 'data/' + auxencode(encodefilename(ndpath)) | |
109 | if len(res) > MAX_PATH_LEN_IN_HGSTORE: |
|
129 | if len(res) > MAX_PATH_LEN_IN_HGSTORE: | |
@@ -155,7 +175,7 b' class basicstore:' | |||||
155 | self.opener.createmode = self.createmode |
|
175 | self.opener.createmode = self.createmode | |
156 |
|
176 | |||
157 | def join(self, f): |
|
177 | def join(self, f): | |
158 | return self.pathjoiner(self.path, f) |
|
178 | return self.pathjoiner(self.path, encodedir(f)) | |
159 |
|
179 | |||
160 | def _walk(self, relpath, recurse): |
|
180 | def _walk(self, relpath, recurse): | |
161 | '''yields (unencoded, encoded, size)''' |
|
181 | '''yields (unencoded, encoded, size)''' | |
@@ -170,7 +190,7 b' class basicstore:' | |||||
170 | fp = self.pathjoiner(p, f) |
|
190 | fp = self.pathjoiner(p, f) | |
171 | if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'): |
|
191 | if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'): | |
172 | n = util.pconvert(fp[striplen:]) |
|
192 | n = util.pconvert(fp[striplen:]) | |
173 | l.append((n, n, st.st_size)) |
|
193 | l.append((decodedir(n), n, st.st_size)) | |
174 | elif kind == stat.S_IFDIR and recurse: |
|
194 | elif kind == stat.S_IFDIR and recurse: | |
175 | visit.append(fp) |
|
195 | visit.append(fp) | |
176 | return sorted(l) |
|
196 | return sorted(l) | |
@@ -215,6 +235,8 b' class encodedstore(basicstore):' | |||||
215 | [self.pathjoiner('store', f) for f in _data.split()]) |
|
235 | [self.pathjoiner('store', f) for f in _data.split()]) | |
216 |
|
236 | |||
217 | class fncache(object): |
|
237 | class fncache(object): | |
|
238 | # the filename used to be partially encoded | |||
|
239 | # hence the encodedir/decodedir dance | |||
218 | def __init__(self, opener): |
|
240 | def __init__(self, opener): | |
219 | self.opener = opener |
|
241 | self.opener = opener | |
220 | self.entries = None |
|
242 | self.entries = None | |
@@ -231,20 +253,20 b' class fncache(object):' | |||||
231 | if (len(line) < 2) or (line[-1] != '\n'): |
|
253 | if (len(line) < 2) or (line[-1] != '\n'): | |
232 | t = _('invalid entry in fncache, line %s') % (n + 1) |
|
254 | t = _('invalid entry in fncache, line %s') % (n + 1) | |
233 | raise util.Abort(t) |
|
255 | raise util.Abort(t) | |
234 | self.entries.add(line[:-1]) |
|
256 | self.entries.add(decodedir(line[:-1])) | |
235 | fp.close() |
|
257 | fp.close() | |
236 |
|
258 | |||
237 | def rewrite(self, files): |
|
259 | def rewrite(self, files): | |
238 | fp = self.opener('fncache', mode='wb') |
|
260 | fp = self.opener('fncache', mode='wb') | |
239 | for p in files: |
|
261 | for p in files: | |
240 | fp.write(p + '\n') |
|
262 | fp.write(encodedir(p) + '\n') | |
241 | fp.close() |
|
263 | fp.close() | |
242 | self.entries = set(files) |
|
264 | self.entries = set(files) | |
243 |
|
265 | |||
244 | def add(self, fn): |
|
266 | def add(self, fn): | |
245 | if self.entries is None: |
|
267 | if self.entries is None: | |
246 | self._load() |
|
268 | self._load() | |
247 | self.opener('fncache', 'ab').write(fn + '\n') |
|
269 | self.opener('fncache', 'ab').write(encodedir(fn) + '\n') | |
248 |
|
270 | |||
249 | def __contains__(self, fn): |
|
271 | def __contains__(self, fn): | |
250 | if self.entries is None: |
|
272 | if self.entries is None: |
@@ -8,6 +8,8 b'' | |||||
8 | import util, error |
|
8 | import util, error | |
9 | from i18n import _ |
|
9 | from i18n import _ | |
10 |
|
10 | |||
|
11 | from mercurial import store | |||
|
12 | ||||
11 | class StreamException(Exception): |
|
13 | class StreamException(Exception): | |
12 | def __init__(self, code): |
|
14 | def __init__(self, code): | |
13 | Exception.__init__(self) |
|
15 | Exception.__init__(self) | |
@@ -46,7 +48,8 b' def stream_out(repo, untrusted=False):' | |||||
46 | try: |
|
48 | try: | |
47 | repo.ui.debug(_('scanning\n')) |
|
49 | repo.ui.debug(_('scanning\n')) | |
48 | for name, ename, size in repo.store.walk(): |
|
50 | for name, ename, size in repo.store.walk(): | |
49 | entries.append((name, size)) |
|
51 | # for backwards compat, name was partially encoded | |
|
52 | entries.append((store.encodedir(name), size)) | |||
50 | total_bytes += size |
|
53 | total_bytes += size | |
51 | finally: |
|
54 | finally: | |
52 | lock.release() |
|
55 | lock.release() |
@@ -36,8 +36,8 b' checking manifests' | |||||
36 | crosschecking files in changesets and manifests |
|
36 | crosschecking files in changesets and manifests | |
37 | checking files |
|
37 | checking files | |
38 | data/a.i@0: missing revlog! |
|
38 | data/a.i@0: missing revlog! | |
39 |
data/a.i.hg |
|
39 | data/a.i.hg/c.i@2: missing revlog! | |
40 |
data/a.i |
|
40 | data/a.i/b.i@1: missing revlog! | |
41 | 3 files, 3 changesets, 3 total revisions |
|
41 | 3 files, 3 changesets, 3 total revisions | |
42 | 3 integrity errors encountered! |
|
42 | 3 integrity errors encountered! | |
43 | (first damaged changeset appears to be 0) |
|
43 | (first damaged changeset appears to be 0) |
General Comments 0
You need to be logged in to leave comments.
Login now