##// END OF EJS Templates
store: exclude `undo.` nodemap's file from `walk`...
marmoute -
r47752:0b569c75 default
parent child Browse files
Show More
@@ -1,810 +1,814 b''
1 # store.py - repository store handling for Mercurial
1 # store.py - repository store handling for Mercurial
2 #
2 #
3 # Copyright 2008 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2008 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import functools
11 import functools
12 import os
12 import os
13 import re
13 import stat
14 import stat
14
15
15 from .i18n import _
16 from .i18n import _
16 from .pycompat import getattr
17 from .pycompat import getattr
17 from .node import hex
18 from .node import hex
18 from . import (
19 from . import (
19 changelog,
20 changelog,
20 error,
21 error,
21 manifest,
22 manifest,
22 policy,
23 policy,
23 pycompat,
24 pycompat,
24 util,
25 util,
25 vfs as vfsmod,
26 vfs as vfsmod,
26 )
27 )
27 from .utils import hashutil
28 from .utils import hashutil
28
29
29 parsers = policy.importmod('parsers')
30 parsers = policy.importmod('parsers')
30 # how much bytes should be read from fncache in one read
31 # how much bytes should be read from fncache in one read
31 # It is done to prevent loading large fncache files into memory
32 # It is done to prevent loading large fncache files into memory
32 fncache_chunksize = 10 ** 6
33 fncache_chunksize = 10 ** 6
33
34
34
35
35 def _matchtrackedpath(path, matcher):
36 def _matchtrackedpath(path, matcher):
36 """parses a fncache entry and returns whether the entry is tracking a path
37 """parses a fncache entry and returns whether the entry is tracking a path
37 matched by matcher or not.
38 matched by matcher or not.
38
39
39 If matcher is None, returns True"""
40 If matcher is None, returns True"""
40
41
41 if matcher is None:
42 if matcher is None:
42 return True
43 return True
43 path = decodedir(path)
44 path = decodedir(path)
44 if path.startswith(b'data/'):
45 if path.startswith(b'data/'):
45 return matcher(path[len(b'data/') : -len(b'.i')])
46 return matcher(path[len(b'data/') : -len(b'.i')])
46 elif path.startswith(b'meta/'):
47 elif path.startswith(b'meta/'):
47 return matcher.visitdir(path[len(b'meta/') : -len(b'/00manifest.i')])
48 return matcher.visitdir(path[len(b'meta/') : -len(b'/00manifest.i')])
48
49
49 raise error.ProgrammingError(b"cannot decode path %s" % path)
50 raise error.ProgrammingError(b"cannot decode path %s" % path)
50
51
51
52
52 # This avoids a collision between a file named foo and a dir named
53 # This avoids a collision between a file named foo and a dir named
53 # foo.i or foo.d
54 # foo.i or foo.d
54 def _encodedir(path):
55 def _encodedir(path):
55 """
56 """
56 >>> _encodedir(b'data/foo.i')
57 >>> _encodedir(b'data/foo.i')
57 'data/foo.i'
58 'data/foo.i'
58 >>> _encodedir(b'data/foo.i/bla.i')
59 >>> _encodedir(b'data/foo.i/bla.i')
59 'data/foo.i.hg/bla.i'
60 'data/foo.i.hg/bla.i'
60 >>> _encodedir(b'data/foo.i.hg/bla.i')
61 >>> _encodedir(b'data/foo.i.hg/bla.i')
61 'data/foo.i.hg.hg/bla.i'
62 'data/foo.i.hg.hg/bla.i'
62 >>> _encodedir(b'data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
63 >>> _encodedir(b'data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
63 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
64 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
64 """
65 """
65 return (
66 return (
66 path.replace(b".hg/", b".hg.hg/")
67 path.replace(b".hg/", b".hg.hg/")
67 .replace(b".i/", b".i.hg/")
68 .replace(b".i/", b".i.hg/")
68 .replace(b".d/", b".d.hg/")
69 .replace(b".d/", b".d.hg/")
69 )
70 )
70
71
71
72
72 encodedir = getattr(parsers, 'encodedir', _encodedir)
73 encodedir = getattr(parsers, 'encodedir', _encodedir)
73
74
74
75
75 def decodedir(path):
76 def decodedir(path):
76 """
77 """
77 >>> decodedir(b'data/foo.i')
78 >>> decodedir(b'data/foo.i')
78 'data/foo.i'
79 'data/foo.i'
79 >>> decodedir(b'data/foo.i.hg/bla.i')
80 >>> decodedir(b'data/foo.i.hg/bla.i')
80 'data/foo.i/bla.i'
81 'data/foo.i/bla.i'
81 >>> decodedir(b'data/foo.i.hg.hg/bla.i')
82 >>> decodedir(b'data/foo.i.hg.hg/bla.i')
82 'data/foo.i.hg/bla.i'
83 'data/foo.i.hg/bla.i'
83 """
84 """
84 if b".hg/" not in path:
85 if b".hg/" not in path:
85 return path
86 return path
86 return (
87 return (
87 path.replace(b".d.hg/", b".d/")
88 path.replace(b".d.hg/", b".d/")
88 .replace(b".i.hg/", b".i/")
89 .replace(b".i.hg/", b".i/")
89 .replace(b".hg.hg/", b".hg/")
90 .replace(b".hg.hg/", b".hg/")
90 )
91 )
91
92
92
93
93 def _reserved():
94 def _reserved():
94 """characters that are problematic for filesystems
95 """characters that are problematic for filesystems
95
96
96 * ascii escapes (0..31)
97 * ascii escapes (0..31)
97 * ascii hi (126..255)
98 * ascii hi (126..255)
98 * windows specials
99 * windows specials
99
100
100 these characters will be escaped by encodefunctions
101 these characters will be escaped by encodefunctions
101 """
102 """
102 winreserved = [ord(x) for x in u'\\:*?"<>|']
103 winreserved = [ord(x) for x in u'\\:*?"<>|']
103 for x in range(32):
104 for x in range(32):
104 yield x
105 yield x
105 for x in range(126, 256):
106 for x in range(126, 256):
106 yield x
107 yield x
107 for x in winreserved:
108 for x in winreserved:
108 yield x
109 yield x
109
110
110
111
111 def _buildencodefun():
112 def _buildencodefun():
112 """
113 """
113 >>> enc, dec = _buildencodefun()
114 >>> enc, dec = _buildencodefun()
114
115
115 >>> enc(b'nothing/special.txt')
116 >>> enc(b'nothing/special.txt')
116 'nothing/special.txt'
117 'nothing/special.txt'
117 >>> dec(b'nothing/special.txt')
118 >>> dec(b'nothing/special.txt')
118 'nothing/special.txt'
119 'nothing/special.txt'
119
120
120 >>> enc(b'HELLO')
121 >>> enc(b'HELLO')
121 '_h_e_l_l_o'
122 '_h_e_l_l_o'
122 >>> dec(b'_h_e_l_l_o')
123 >>> dec(b'_h_e_l_l_o')
123 'HELLO'
124 'HELLO'
124
125
125 >>> enc(b'hello:world?')
126 >>> enc(b'hello:world?')
126 'hello~3aworld~3f'
127 'hello~3aworld~3f'
127 >>> dec(b'hello~3aworld~3f')
128 >>> dec(b'hello~3aworld~3f')
128 'hello:world?'
129 'hello:world?'
129
130
130 >>> enc(b'the\\x07quick\\xADshot')
131 >>> enc(b'the\\x07quick\\xADshot')
131 'the~07quick~adshot'
132 'the~07quick~adshot'
132 >>> dec(b'the~07quick~adshot')
133 >>> dec(b'the~07quick~adshot')
133 'the\\x07quick\\xadshot'
134 'the\\x07quick\\xadshot'
134 """
135 """
135 e = b'_'
136 e = b'_'
136 xchr = pycompat.bytechr
137 xchr = pycompat.bytechr
137 asciistr = list(map(xchr, range(127)))
138 asciistr = list(map(xchr, range(127)))
138 capitals = list(range(ord(b"A"), ord(b"Z") + 1))
139 capitals = list(range(ord(b"A"), ord(b"Z") + 1))
139
140
140 cmap = {x: x for x in asciistr}
141 cmap = {x: x for x in asciistr}
141 for x in _reserved():
142 for x in _reserved():
142 cmap[xchr(x)] = b"~%02x" % x
143 cmap[xchr(x)] = b"~%02x" % x
143 for x in capitals + [ord(e)]:
144 for x in capitals + [ord(e)]:
144 cmap[xchr(x)] = e + xchr(x).lower()
145 cmap[xchr(x)] = e + xchr(x).lower()
145
146
146 dmap = {}
147 dmap = {}
147 for k, v in pycompat.iteritems(cmap):
148 for k, v in pycompat.iteritems(cmap):
148 dmap[v] = k
149 dmap[v] = k
149
150
150 def decode(s):
151 def decode(s):
151 i = 0
152 i = 0
152 while i < len(s):
153 while i < len(s):
153 for l in pycompat.xrange(1, 4):
154 for l in pycompat.xrange(1, 4):
154 try:
155 try:
155 yield dmap[s[i : i + l]]
156 yield dmap[s[i : i + l]]
156 i += l
157 i += l
157 break
158 break
158 except KeyError:
159 except KeyError:
159 pass
160 pass
160 else:
161 else:
161 raise KeyError
162 raise KeyError
162
163
163 return (
164 return (
164 lambda s: b''.join(
165 lambda s: b''.join(
165 [cmap[s[c : c + 1]] for c in pycompat.xrange(len(s))]
166 [cmap[s[c : c + 1]] for c in pycompat.xrange(len(s))]
166 ),
167 ),
167 lambda s: b''.join(list(decode(s))),
168 lambda s: b''.join(list(decode(s))),
168 )
169 )
169
170
170
171
171 _encodefname, _decodefname = _buildencodefun()
172 _encodefname, _decodefname = _buildencodefun()
172
173
173
174
174 def encodefilename(s):
175 def encodefilename(s):
175 """
176 """
176 >>> encodefilename(b'foo.i/bar.d/bla.hg/hi:world?/HELLO')
177 >>> encodefilename(b'foo.i/bar.d/bla.hg/hi:world?/HELLO')
177 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
178 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
178 """
179 """
179 return _encodefname(encodedir(s))
180 return _encodefname(encodedir(s))
180
181
181
182
182 def decodefilename(s):
183 def decodefilename(s):
183 """
184 """
184 >>> decodefilename(b'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
185 >>> decodefilename(b'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
185 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
186 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
186 """
187 """
187 return decodedir(_decodefname(s))
188 return decodedir(_decodefname(s))
188
189
189
190
190 def _buildlowerencodefun():
191 def _buildlowerencodefun():
191 """
192 """
192 >>> f = _buildlowerencodefun()
193 >>> f = _buildlowerencodefun()
193 >>> f(b'nothing/special.txt')
194 >>> f(b'nothing/special.txt')
194 'nothing/special.txt'
195 'nothing/special.txt'
195 >>> f(b'HELLO')
196 >>> f(b'HELLO')
196 'hello'
197 'hello'
197 >>> f(b'hello:world?')
198 >>> f(b'hello:world?')
198 'hello~3aworld~3f'
199 'hello~3aworld~3f'
199 >>> f(b'the\\x07quick\\xADshot')
200 >>> f(b'the\\x07quick\\xADshot')
200 'the~07quick~adshot'
201 'the~07quick~adshot'
201 """
202 """
202 xchr = pycompat.bytechr
203 xchr = pycompat.bytechr
203 cmap = {xchr(x): xchr(x) for x in pycompat.xrange(127)}
204 cmap = {xchr(x): xchr(x) for x in pycompat.xrange(127)}
204 for x in _reserved():
205 for x in _reserved():
205 cmap[xchr(x)] = b"~%02x" % x
206 cmap[xchr(x)] = b"~%02x" % x
206 for x in range(ord(b"A"), ord(b"Z") + 1):
207 for x in range(ord(b"A"), ord(b"Z") + 1):
207 cmap[xchr(x)] = xchr(x).lower()
208 cmap[xchr(x)] = xchr(x).lower()
208
209
209 def lowerencode(s):
210 def lowerencode(s):
210 return b"".join([cmap[c] for c in pycompat.iterbytestr(s)])
211 return b"".join([cmap[c] for c in pycompat.iterbytestr(s)])
211
212
212 return lowerencode
213 return lowerencode
213
214
214
215
215 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
216 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
216
217
217 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
218 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
218 _winres3 = (b'aux', b'con', b'prn', b'nul') # length 3
219 _winres3 = (b'aux', b'con', b'prn', b'nul') # length 3
219 _winres4 = (b'com', b'lpt') # length 4 (with trailing 1..9)
220 _winres4 = (b'com', b'lpt') # length 4 (with trailing 1..9)
220
221
221
222
222 def _auxencode(path, dotencode):
223 def _auxencode(path, dotencode):
223 """
224 """
224 Encodes filenames containing names reserved by Windows or which end in
225 Encodes filenames containing names reserved by Windows or which end in
225 period or space. Does not touch other single reserved characters c.
226 period or space. Does not touch other single reserved characters c.
226 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
227 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
227 Additionally encodes space or period at the beginning, if dotencode is
228 Additionally encodes space or period at the beginning, if dotencode is
228 True. Parameter path is assumed to be all lowercase.
229 True. Parameter path is assumed to be all lowercase.
229 A segment only needs encoding if a reserved name appears as a
230 A segment only needs encoding if a reserved name appears as a
230 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
231 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
231 doesn't need encoding.
232 doesn't need encoding.
232
233
233 >>> s = b'.foo/aux.txt/txt.aux/con/prn/nul/foo.'
234 >>> s = b'.foo/aux.txt/txt.aux/con/prn/nul/foo.'
234 >>> _auxencode(s.split(b'/'), True)
235 >>> _auxencode(s.split(b'/'), True)
235 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
236 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
236 >>> s = b'.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
237 >>> s = b'.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
237 >>> _auxencode(s.split(b'/'), False)
238 >>> _auxencode(s.split(b'/'), False)
238 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
239 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
239 >>> _auxencode([b'foo. '], True)
240 >>> _auxencode([b'foo. '], True)
240 ['foo.~20']
241 ['foo.~20']
241 >>> _auxencode([b' .foo'], True)
242 >>> _auxencode([b' .foo'], True)
242 ['~20.foo']
243 ['~20.foo']
243 """
244 """
244 for i, n in enumerate(path):
245 for i, n in enumerate(path):
245 if not n:
246 if not n:
246 continue
247 continue
247 if dotencode and n[0] in b'. ':
248 if dotencode and n[0] in b'. ':
248 n = b"~%02x" % ord(n[0:1]) + n[1:]
249 n = b"~%02x" % ord(n[0:1]) + n[1:]
249 path[i] = n
250 path[i] = n
250 else:
251 else:
251 l = n.find(b'.')
252 l = n.find(b'.')
252 if l == -1:
253 if l == -1:
253 l = len(n)
254 l = len(n)
254 if (l == 3 and n[:3] in _winres3) or (
255 if (l == 3 and n[:3] in _winres3) or (
255 l == 4
256 l == 4
256 and n[3:4] <= b'9'
257 and n[3:4] <= b'9'
257 and n[3:4] >= b'1'
258 and n[3:4] >= b'1'
258 and n[:3] in _winres4
259 and n[:3] in _winres4
259 ):
260 ):
260 # encode third letter ('aux' -> 'au~78')
261 # encode third letter ('aux' -> 'au~78')
261 ec = b"~%02x" % ord(n[2:3])
262 ec = b"~%02x" % ord(n[2:3])
262 n = n[0:2] + ec + n[3:]
263 n = n[0:2] + ec + n[3:]
263 path[i] = n
264 path[i] = n
264 if n[-1] in b'. ':
265 if n[-1] in b'. ':
265 # encode last period or space ('foo...' -> 'foo..~2e')
266 # encode last period or space ('foo...' -> 'foo..~2e')
266 path[i] = n[:-1] + b"~%02x" % ord(n[-1:])
267 path[i] = n[:-1] + b"~%02x" % ord(n[-1:])
267 return path
268 return path
268
269
269
270
270 _maxstorepathlen = 120
271 _maxstorepathlen = 120
271 _dirprefixlen = 8
272 _dirprefixlen = 8
272 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
273 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
273
274
274
275
275 def _hashencode(path, dotencode):
276 def _hashencode(path, dotencode):
276 digest = hex(hashutil.sha1(path).digest())
277 digest = hex(hashutil.sha1(path).digest())
277 le = lowerencode(path[5:]).split(b'/') # skips prefix 'data/' or 'meta/'
278 le = lowerencode(path[5:]).split(b'/') # skips prefix 'data/' or 'meta/'
278 parts = _auxencode(le, dotencode)
279 parts = _auxencode(le, dotencode)
279 basename = parts[-1]
280 basename = parts[-1]
280 _root, ext = os.path.splitext(basename)
281 _root, ext = os.path.splitext(basename)
281 sdirs = []
282 sdirs = []
282 sdirslen = 0
283 sdirslen = 0
283 for p in parts[:-1]:
284 for p in parts[:-1]:
284 d = p[:_dirprefixlen]
285 d = p[:_dirprefixlen]
285 if d[-1] in b'. ':
286 if d[-1] in b'. ':
286 # Windows can't access dirs ending in period or space
287 # Windows can't access dirs ending in period or space
287 d = d[:-1] + b'_'
288 d = d[:-1] + b'_'
288 if sdirslen == 0:
289 if sdirslen == 0:
289 t = len(d)
290 t = len(d)
290 else:
291 else:
291 t = sdirslen + 1 + len(d)
292 t = sdirslen + 1 + len(d)
292 if t > _maxshortdirslen:
293 if t > _maxshortdirslen:
293 break
294 break
294 sdirs.append(d)
295 sdirs.append(d)
295 sdirslen = t
296 sdirslen = t
296 dirs = b'/'.join(sdirs)
297 dirs = b'/'.join(sdirs)
297 if len(dirs) > 0:
298 if len(dirs) > 0:
298 dirs += b'/'
299 dirs += b'/'
299 res = b'dh/' + dirs + digest + ext
300 res = b'dh/' + dirs + digest + ext
300 spaceleft = _maxstorepathlen - len(res)
301 spaceleft = _maxstorepathlen - len(res)
301 if spaceleft > 0:
302 if spaceleft > 0:
302 filler = basename[:spaceleft]
303 filler = basename[:spaceleft]
303 res = b'dh/' + dirs + filler + digest + ext
304 res = b'dh/' + dirs + filler + digest + ext
304 return res
305 return res
305
306
306
307
307 def _hybridencode(path, dotencode):
308 def _hybridencode(path, dotencode):
308 """encodes path with a length limit
309 """encodes path with a length limit
309
310
310 Encodes all paths that begin with 'data/', according to the following.
311 Encodes all paths that begin with 'data/', according to the following.
311
312
312 Default encoding (reversible):
313 Default encoding (reversible):
313
314
314 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
315 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
315 characters are encoded as '~xx', where xx is the two digit hex code
316 characters are encoded as '~xx', where xx is the two digit hex code
316 of the character (see encodefilename).
317 of the character (see encodefilename).
317 Relevant path components consisting of Windows reserved filenames are
318 Relevant path components consisting of Windows reserved filenames are
318 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
319 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
319
320
320 Hashed encoding (not reversible):
321 Hashed encoding (not reversible):
321
322
322 If the default-encoded path is longer than _maxstorepathlen, a
323 If the default-encoded path is longer than _maxstorepathlen, a
323 non-reversible hybrid hashing of the path is done instead.
324 non-reversible hybrid hashing of the path is done instead.
324 This encoding uses up to _dirprefixlen characters of all directory
325 This encoding uses up to _dirprefixlen characters of all directory
325 levels of the lowerencoded path, but not more levels than can fit into
326 levels of the lowerencoded path, but not more levels than can fit into
326 _maxshortdirslen.
327 _maxshortdirslen.
327 Then follows the filler followed by the sha digest of the full path.
328 Then follows the filler followed by the sha digest of the full path.
328 The filler is the beginning of the basename of the lowerencoded path
329 The filler is the beginning of the basename of the lowerencoded path
329 (the basename is everything after the last path separator). The filler
330 (the basename is everything after the last path separator). The filler
330 is as long as possible, filling in characters from the basename until
331 is as long as possible, filling in characters from the basename until
331 the encoded path has _maxstorepathlen characters (or all chars of the
332 the encoded path has _maxstorepathlen characters (or all chars of the
332 basename have been taken).
333 basename have been taken).
333 The extension (e.g. '.i' or '.d') is preserved.
334 The extension (e.g. '.i' or '.d') is preserved.
334
335
335 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
336 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
336 encoding was used.
337 encoding was used.
337 """
338 """
338 path = encodedir(path)
339 path = encodedir(path)
339 ef = _encodefname(path).split(b'/')
340 ef = _encodefname(path).split(b'/')
340 res = b'/'.join(_auxencode(ef, dotencode))
341 res = b'/'.join(_auxencode(ef, dotencode))
341 if len(res) > _maxstorepathlen:
342 if len(res) > _maxstorepathlen:
342 res = _hashencode(path, dotencode)
343 res = _hashencode(path, dotencode)
343 return res
344 return res
344
345
345
346
346 def _pathencode(path):
347 def _pathencode(path):
347 de = encodedir(path)
348 de = encodedir(path)
348 if len(path) > _maxstorepathlen:
349 if len(path) > _maxstorepathlen:
349 return _hashencode(de, True)
350 return _hashencode(de, True)
350 ef = _encodefname(de).split(b'/')
351 ef = _encodefname(de).split(b'/')
351 res = b'/'.join(_auxencode(ef, True))
352 res = b'/'.join(_auxencode(ef, True))
352 if len(res) > _maxstorepathlen:
353 if len(res) > _maxstorepathlen:
353 return _hashencode(de, True)
354 return _hashencode(de, True)
354 return res
355 return res
355
356
356
357
357 _pathencode = getattr(parsers, 'pathencode', _pathencode)
358 _pathencode = getattr(parsers, 'pathencode', _pathencode)
358
359
359
360
360 def _plainhybridencode(f):
361 def _plainhybridencode(f):
361 return _hybridencode(f, False)
362 return _hybridencode(f, False)
362
363
363
364
364 def _calcmode(vfs):
365 def _calcmode(vfs):
365 try:
366 try:
366 # files in .hg/ will be created using this mode
367 # files in .hg/ will be created using this mode
367 mode = vfs.stat().st_mode
368 mode = vfs.stat().st_mode
368 # avoid some useless chmods
369 # avoid some useless chmods
369 if (0o777 & ~util.umask) == (0o777 & mode):
370 if (0o777 & ~util.umask) == (0o777 & mode):
370 mode = None
371 mode = None
371 except OSError:
372 except OSError:
372 mode = None
373 mode = None
373 return mode
374 return mode
374
375
375
376
376 _data = [
377 _data = [
377 b'bookmarks',
378 b'bookmarks',
378 b'narrowspec',
379 b'narrowspec',
379 b'data',
380 b'data',
380 b'meta',
381 b'meta',
381 b'00manifest.d',
382 b'00manifest.d',
382 b'00manifest.i',
383 b'00manifest.i',
383 b'00changelog.d',
384 b'00changelog.d',
384 b'00changelog.i',
385 b'00changelog.i',
385 b'phaseroots',
386 b'phaseroots',
386 b'obsstore',
387 b'obsstore',
387 b'requires',
388 b'requires',
388 ]
389 ]
389
390
390 REVLOG_FILES_MAIN_EXT = (b'.i', b'i.tmpcensored')
391 REVLOG_FILES_MAIN_EXT = (b'.i', b'i.tmpcensored')
391 REVLOG_FILES_OTHER_EXT = (b'.d', b'.n', b'.nd', b'd.tmpcensored')
392 REVLOG_FILES_OTHER_EXT = (b'.d', b'.n', b'.nd', b'd.tmpcensored')
392 # files that are "volatile" and might change between listing and streaming
393 # files that are "volatile" and might change between listing and streaming
393 #
394 #
394 # note: the ".nd" file are nodemap data and won't "change" but they might be
395 # note: the ".nd" file are nodemap data and won't "change" but they might be
395 # deleted.
396 # deleted.
396 REVLOG_FILES_VOLATILE_EXT = (b'.n', b'.nd')
397 REVLOG_FILES_VOLATILE_EXT = (b'.n', b'.nd')
397
398
399 # some exception to the above matching
400 EXCLUDED = re.compile(b'.*undo\.[^/]+\.nd?$')
401
398
402
399 def is_revlog(f, kind, st):
403 def is_revlog(f, kind, st):
400 if kind != stat.S_IFREG:
404 if kind != stat.S_IFREG:
401 return None
405 return None
402 return revlog_type(f)
406 return revlog_type(f)
403
407
404
408
405 def revlog_type(f):
409 def revlog_type(f):
406 if f.endswith(REVLOG_FILES_MAIN_EXT):
410 if f.endswith(REVLOG_FILES_MAIN_EXT):
407 return FILEFLAGS_REVLOG_MAIN
411 return FILEFLAGS_REVLOG_MAIN
408 elif f.endswith(REVLOG_FILES_OTHER_EXT):
412 elif f.endswith(REVLOG_FILES_OTHER_EXT) and EXCLUDED.match(f) is None:
409 t = FILETYPE_FILELOG_OTHER
413 t = FILETYPE_FILELOG_OTHER
410 if f.endswith(REVLOG_FILES_VOLATILE_EXT):
414 if f.endswith(REVLOG_FILES_VOLATILE_EXT):
411 t |= FILEFLAGS_VOLATILE
415 t |= FILEFLAGS_VOLATILE
412 return t
416 return t
413
417
414
418
415 # the file is part of changelog data
419 # the file is part of changelog data
416 FILEFLAGS_CHANGELOG = 1 << 13
420 FILEFLAGS_CHANGELOG = 1 << 13
417 # the file is part of manifest data
421 # the file is part of manifest data
418 FILEFLAGS_MANIFESTLOG = 1 << 12
422 FILEFLAGS_MANIFESTLOG = 1 << 12
419 # the file is part of filelog data
423 # the file is part of filelog data
420 FILEFLAGS_FILELOG = 1 << 11
424 FILEFLAGS_FILELOG = 1 << 11
421 # file that are not directly part of a revlog
425 # file that are not directly part of a revlog
422 FILEFLAGS_OTHER = 1 << 10
426 FILEFLAGS_OTHER = 1 << 10
423
427
424 # the main entry point for a revlog
428 # the main entry point for a revlog
425 FILEFLAGS_REVLOG_MAIN = 1 << 1
429 FILEFLAGS_REVLOG_MAIN = 1 << 1
426 # a secondary file for a revlog
430 # a secondary file for a revlog
427 FILEFLAGS_REVLOG_OTHER = 1 << 0
431 FILEFLAGS_REVLOG_OTHER = 1 << 0
428
432
429 # files that are "volatile" and might change between listing and streaming
433 # files that are "volatile" and might change between listing and streaming
430 FILEFLAGS_VOLATILE = 1 << 20
434 FILEFLAGS_VOLATILE = 1 << 20
431
435
432 FILETYPE_CHANGELOG_MAIN = FILEFLAGS_CHANGELOG | FILEFLAGS_REVLOG_MAIN
436 FILETYPE_CHANGELOG_MAIN = FILEFLAGS_CHANGELOG | FILEFLAGS_REVLOG_MAIN
433 FILETYPE_CHANGELOG_OTHER = FILEFLAGS_CHANGELOG | FILEFLAGS_REVLOG_OTHER
437 FILETYPE_CHANGELOG_OTHER = FILEFLAGS_CHANGELOG | FILEFLAGS_REVLOG_OTHER
434 FILETYPE_MANIFESTLOG_MAIN = FILEFLAGS_MANIFESTLOG | FILEFLAGS_REVLOG_MAIN
438 FILETYPE_MANIFESTLOG_MAIN = FILEFLAGS_MANIFESTLOG | FILEFLAGS_REVLOG_MAIN
435 FILETYPE_MANIFESTLOG_OTHER = FILEFLAGS_MANIFESTLOG | FILEFLAGS_REVLOG_OTHER
439 FILETYPE_MANIFESTLOG_OTHER = FILEFLAGS_MANIFESTLOG | FILEFLAGS_REVLOG_OTHER
436 FILETYPE_FILELOG_MAIN = FILEFLAGS_FILELOG | FILEFLAGS_REVLOG_MAIN
440 FILETYPE_FILELOG_MAIN = FILEFLAGS_FILELOG | FILEFLAGS_REVLOG_MAIN
437 FILETYPE_FILELOG_OTHER = FILEFLAGS_FILELOG | FILEFLAGS_REVLOG_OTHER
441 FILETYPE_FILELOG_OTHER = FILEFLAGS_FILELOG | FILEFLAGS_REVLOG_OTHER
438 FILETYPE_OTHER = FILEFLAGS_OTHER
442 FILETYPE_OTHER = FILEFLAGS_OTHER
439
443
440
444
441 class basicstore(object):
445 class basicstore(object):
442 '''base class for local repository stores'''
446 '''base class for local repository stores'''
443
447
444 def __init__(self, path, vfstype):
448 def __init__(self, path, vfstype):
445 vfs = vfstype(path)
449 vfs = vfstype(path)
446 self.path = vfs.base
450 self.path = vfs.base
447 self.createmode = _calcmode(vfs)
451 self.createmode = _calcmode(vfs)
448 vfs.createmode = self.createmode
452 vfs.createmode = self.createmode
449 self.rawvfs = vfs
453 self.rawvfs = vfs
450 self.vfs = vfsmod.filtervfs(vfs, encodedir)
454 self.vfs = vfsmod.filtervfs(vfs, encodedir)
451 self.opener = self.vfs
455 self.opener = self.vfs
452
456
453 def join(self, f):
457 def join(self, f):
454 return self.path + b'/' + encodedir(f)
458 return self.path + b'/' + encodedir(f)
455
459
456 def _walk(self, relpath, recurse):
460 def _walk(self, relpath, recurse):
457 '''yields (unencoded, encoded, size)'''
461 '''yields (unencoded, encoded, size)'''
458 path = self.path
462 path = self.path
459 if relpath:
463 if relpath:
460 path += b'/' + relpath
464 path += b'/' + relpath
461 striplen = len(self.path) + 1
465 striplen = len(self.path) + 1
462 l = []
466 l = []
463 if self.rawvfs.isdir(path):
467 if self.rawvfs.isdir(path):
464 visit = [path]
468 visit = [path]
465 readdir = self.rawvfs.readdir
469 readdir = self.rawvfs.readdir
466 while visit:
470 while visit:
467 p = visit.pop()
471 p = visit.pop()
468 for f, kind, st in readdir(p, stat=True):
472 for f, kind, st in readdir(p, stat=True):
469 fp = p + b'/' + f
473 fp = p + b'/' + f
470 rl_type = is_revlog(f, kind, st)
474 rl_type = is_revlog(f, kind, st)
471 if rl_type is not None:
475 if rl_type is not None:
472 n = util.pconvert(fp[striplen:])
476 n = util.pconvert(fp[striplen:])
473 l.append((rl_type, decodedir(n), n, st.st_size))
477 l.append((rl_type, decodedir(n), n, st.st_size))
474 elif kind == stat.S_IFDIR and recurse:
478 elif kind == stat.S_IFDIR and recurse:
475 visit.append(fp)
479 visit.append(fp)
476 l.sort()
480 l.sort()
477 return l
481 return l
478
482
479 def changelog(self, trypending, concurrencychecker=None):
483 def changelog(self, trypending, concurrencychecker=None):
480 return changelog.changelog(
484 return changelog.changelog(
481 self.vfs,
485 self.vfs,
482 trypending=trypending,
486 trypending=trypending,
483 concurrencychecker=concurrencychecker,
487 concurrencychecker=concurrencychecker,
484 )
488 )
485
489
486 def manifestlog(self, repo, storenarrowmatch):
490 def manifestlog(self, repo, storenarrowmatch):
487 rootstore = manifest.manifestrevlog(repo.nodeconstants, self.vfs)
491 rootstore = manifest.manifestrevlog(repo.nodeconstants, self.vfs)
488 return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch)
492 return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch)
489
493
490 def datafiles(self, matcher=None):
494 def datafiles(self, matcher=None):
491 files = self._walk(b'data', True) + self._walk(b'meta', True)
495 files = self._walk(b'data', True) + self._walk(b'meta', True)
492 for (t, u, e, s) in files:
496 for (t, u, e, s) in files:
493 yield (FILEFLAGS_FILELOG | t, u, e, s)
497 yield (FILEFLAGS_FILELOG | t, u, e, s)
494
498
495 def topfiles(self):
499 def topfiles(self):
496 # yield manifest before changelog
500 # yield manifest before changelog
497 files = reversed(self._walk(b'', False))
501 files = reversed(self._walk(b'', False))
498 for (t, u, e, s) in files:
502 for (t, u, e, s) in files:
499 if u.startswith(b'00changelog'):
503 if u.startswith(b'00changelog'):
500 yield (FILEFLAGS_CHANGELOG | t, u, e, s)
504 yield (FILEFLAGS_CHANGELOG | t, u, e, s)
501 elif u.startswith(b'00manifest'):
505 elif u.startswith(b'00manifest'):
502 yield (FILEFLAGS_MANIFESTLOG | t, u, e, s)
506 yield (FILEFLAGS_MANIFESTLOG | t, u, e, s)
503 else:
507 else:
504 yield (FILETYPE_OTHER | t, u, e, s)
508 yield (FILETYPE_OTHER | t, u, e, s)
505
509
506 def walk(self, matcher=None):
510 def walk(self, matcher=None):
507 """return file related to data storage (ie: revlogs)
511 """return file related to data storage (ie: revlogs)
508
512
509 yields (file_type, unencoded, encoded, size)
513 yields (file_type, unencoded, encoded, size)
510
514
511 if a matcher is passed, storage files of only those tracked paths
515 if a matcher is passed, storage files of only those tracked paths
512 are passed with matches the matcher
516 are passed with matches the matcher
513 """
517 """
514 # yield data files first
518 # yield data files first
515 for x in self.datafiles(matcher):
519 for x in self.datafiles(matcher):
516 yield x
520 yield x
517 for x in self.topfiles():
521 for x in self.topfiles():
518 yield x
522 yield x
519
523
520 def copylist(self):
524 def copylist(self):
521 return _data
525 return _data
522
526
523 def write(self, tr):
527 def write(self, tr):
524 pass
528 pass
525
529
526 def invalidatecaches(self):
530 def invalidatecaches(self):
527 pass
531 pass
528
532
529 def markremoved(self, fn):
533 def markremoved(self, fn):
530 pass
534 pass
531
535
532 def __contains__(self, path):
536 def __contains__(self, path):
533 '''Checks if the store contains path'''
537 '''Checks if the store contains path'''
534 path = b"/".join((b"data", path))
538 path = b"/".join((b"data", path))
535 # file?
539 # file?
536 if self.vfs.exists(path + b".i"):
540 if self.vfs.exists(path + b".i"):
537 return True
541 return True
538 # dir?
542 # dir?
539 if not path.endswith(b"/"):
543 if not path.endswith(b"/"):
540 path = path + b"/"
544 path = path + b"/"
541 return self.vfs.exists(path)
545 return self.vfs.exists(path)
542
546
543
547
544 class encodedstore(basicstore):
548 class encodedstore(basicstore):
545 def __init__(self, path, vfstype):
549 def __init__(self, path, vfstype):
546 vfs = vfstype(path + b'/store')
550 vfs = vfstype(path + b'/store')
547 self.path = vfs.base
551 self.path = vfs.base
548 self.createmode = _calcmode(vfs)
552 self.createmode = _calcmode(vfs)
549 vfs.createmode = self.createmode
553 vfs.createmode = self.createmode
550 self.rawvfs = vfs
554 self.rawvfs = vfs
551 self.vfs = vfsmod.filtervfs(vfs, encodefilename)
555 self.vfs = vfsmod.filtervfs(vfs, encodefilename)
552 self.opener = self.vfs
556 self.opener = self.vfs
553
557
554 def datafiles(self, matcher=None):
558 def datafiles(self, matcher=None):
555 for t, a, b, size in super(encodedstore, self).datafiles():
559 for t, a, b, size in super(encodedstore, self).datafiles():
556 try:
560 try:
557 a = decodefilename(a)
561 a = decodefilename(a)
558 except KeyError:
562 except KeyError:
559 a = None
563 a = None
560 if a is not None and not _matchtrackedpath(a, matcher):
564 if a is not None and not _matchtrackedpath(a, matcher):
561 continue
565 continue
562 yield t, a, b, size
566 yield t, a, b, size
563
567
564 def join(self, f):
568 def join(self, f):
565 return self.path + b'/' + encodefilename(f)
569 return self.path + b'/' + encodefilename(f)
566
570
567 def copylist(self):
571 def copylist(self):
568 return [b'requires', b'00changelog.i'] + [b'store/' + f for f in _data]
572 return [b'requires', b'00changelog.i'] + [b'store/' + f for f in _data]
569
573
570
574
571 class fncache(object):
575 class fncache(object):
572 # the filename used to be partially encoded
576 # the filename used to be partially encoded
573 # hence the encodedir/decodedir dance
577 # hence the encodedir/decodedir dance
574 def __init__(self, vfs):
578 def __init__(self, vfs):
575 self.vfs = vfs
579 self.vfs = vfs
576 self.entries = None
580 self.entries = None
577 self._dirty = False
581 self._dirty = False
578 # set of new additions to fncache
582 # set of new additions to fncache
579 self.addls = set()
583 self.addls = set()
580
584
581 def ensureloaded(self, warn=None):
585 def ensureloaded(self, warn=None):
582 """read the fncache file if not already read.
586 """read the fncache file if not already read.
583
587
584 If the file on disk is corrupted, raise. If warn is provided,
588 If the file on disk is corrupted, raise. If warn is provided,
585 warn and keep going instead."""
589 warn and keep going instead."""
586 if self.entries is None:
590 if self.entries is None:
587 self._load(warn)
591 self._load(warn)
588
592
589 def _load(self, warn=None):
593 def _load(self, warn=None):
590 '''fill the entries from the fncache file'''
594 '''fill the entries from the fncache file'''
591 self._dirty = False
595 self._dirty = False
592 try:
596 try:
593 fp = self.vfs(b'fncache', mode=b'rb')
597 fp = self.vfs(b'fncache', mode=b'rb')
594 except IOError:
598 except IOError:
595 # skip nonexistent file
599 # skip nonexistent file
596 self.entries = set()
600 self.entries = set()
597 return
601 return
598
602
599 self.entries = set()
603 self.entries = set()
600 chunk = b''
604 chunk = b''
601 for c in iter(functools.partial(fp.read, fncache_chunksize), b''):
605 for c in iter(functools.partial(fp.read, fncache_chunksize), b''):
602 chunk += c
606 chunk += c
603 try:
607 try:
604 p = chunk.rindex(b'\n')
608 p = chunk.rindex(b'\n')
605 self.entries.update(decodedir(chunk[: p + 1]).splitlines())
609 self.entries.update(decodedir(chunk[: p + 1]).splitlines())
606 chunk = chunk[p + 1 :]
610 chunk = chunk[p + 1 :]
607 except ValueError:
611 except ValueError:
608 # substring '\n' not found, maybe the entry is bigger than the
612 # substring '\n' not found, maybe the entry is bigger than the
609 # chunksize, so let's keep iterating
613 # chunksize, so let's keep iterating
610 pass
614 pass
611
615
612 if chunk:
616 if chunk:
613 msg = _(b"fncache does not ends with a newline")
617 msg = _(b"fncache does not ends with a newline")
614 if warn:
618 if warn:
615 warn(msg + b'\n')
619 warn(msg + b'\n')
616 else:
620 else:
617 raise error.Abort(
621 raise error.Abort(
618 msg,
622 msg,
619 hint=_(
623 hint=_(
620 b"use 'hg debugrebuildfncache' to "
624 b"use 'hg debugrebuildfncache' to "
621 b"rebuild the fncache"
625 b"rebuild the fncache"
622 ),
626 ),
623 )
627 )
624 self._checkentries(fp, warn)
628 self._checkentries(fp, warn)
625 fp.close()
629 fp.close()
626
630
627 def _checkentries(self, fp, warn):
631 def _checkentries(self, fp, warn):
628 """ make sure there is no empty string in entries """
632 """ make sure there is no empty string in entries """
629 if b'' in self.entries:
633 if b'' in self.entries:
630 fp.seek(0)
634 fp.seek(0)
631 for n, line in enumerate(util.iterfile(fp)):
635 for n, line in enumerate(util.iterfile(fp)):
632 if not line.rstrip(b'\n'):
636 if not line.rstrip(b'\n'):
633 t = _(b'invalid entry in fncache, line %d') % (n + 1)
637 t = _(b'invalid entry in fncache, line %d') % (n + 1)
634 if warn:
638 if warn:
635 warn(t + b'\n')
639 warn(t + b'\n')
636 else:
640 else:
637 raise error.Abort(t)
641 raise error.Abort(t)
638
642
639 def write(self, tr):
643 def write(self, tr):
640 if self._dirty:
644 if self._dirty:
641 assert self.entries is not None
645 assert self.entries is not None
642 self.entries = self.entries | self.addls
646 self.entries = self.entries | self.addls
643 self.addls = set()
647 self.addls = set()
644 tr.addbackup(b'fncache')
648 tr.addbackup(b'fncache')
645 fp = self.vfs(b'fncache', mode=b'wb', atomictemp=True)
649 fp = self.vfs(b'fncache', mode=b'wb', atomictemp=True)
646 if self.entries:
650 if self.entries:
647 fp.write(encodedir(b'\n'.join(self.entries) + b'\n'))
651 fp.write(encodedir(b'\n'.join(self.entries) + b'\n'))
648 fp.close()
652 fp.close()
649 self._dirty = False
653 self._dirty = False
650 if self.addls:
654 if self.addls:
651 # if we have just new entries, let's append them to the fncache
655 # if we have just new entries, let's append them to the fncache
652 tr.addbackup(b'fncache')
656 tr.addbackup(b'fncache')
653 fp = self.vfs(b'fncache', mode=b'ab', atomictemp=True)
657 fp = self.vfs(b'fncache', mode=b'ab', atomictemp=True)
654 if self.addls:
658 if self.addls:
655 fp.write(encodedir(b'\n'.join(self.addls) + b'\n'))
659 fp.write(encodedir(b'\n'.join(self.addls) + b'\n'))
656 fp.close()
660 fp.close()
657 self.entries = None
661 self.entries = None
658 self.addls = set()
662 self.addls = set()
659
663
660 def add(self, fn):
664 def add(self, fn):
661 if self.entries is None:
665 if self.entries is None:
662 self._load()
666 self._load()
663 if fn not in self.entries:
667 if fn not in self.entries:
664 self.addls.add(fn)
668 self.addls.add(fn)
665
669
666 def remove(self, fn):
670 def remove(self, fn):
667 if self.entries is None:
671 if self.entries is None:
668 self._load()
672 self._load()
669 if fn in self.addls:
673 if fn in self.addls:
670 self.addls.remove(fn)
674 self.addls.remove(fn)
671 return
675 return
672 try:
676 try:
673 self.entries.remove(fn)
677 self.entries.remove(fn)
674 self._dirty = True
678 self._dirty = True
675 except KeyError:
679 except KeyError:
676 pass
680 pass
677
681
678 def __contains__(self, fn):
682 def __contains__(self, fn):
679 if fn in self.addls:
683 if fn in self.addls:
680 return True
684 return True
681 if self.entries is None:
685 if self.entries is None:
682 self._load()
686 self._load()
683 return fn in self.entries
687 return fn in self.entries
684
688
685 def __iter__(self):
689 def __iter__(self):
686 if self.entries is None:
690 if self.entries is None:
687 self._load()
691 self._load()
688 return iter(self.entries | self.addls)
692 return iter(self.entries | self.addls)
689
693
690
694
691 class _fncachevfs(vfsmod.proxyvfs):
695 class _fncachevfs(vfsmod.proxyvfs):
692 def __init__(self, vfs, fnc, encode):
696 def __init__(self, vfs, fnc, encode):
693 vfsmod.proxyvfs.__init__(self, vfs)
697 vfsmod.proxyvfs.__init__(self, vfs)
694 self.fncache = fnc
698 self.fncache = fnc
695 self.encode = encode
699 self.encode = encode
696
700
697 def __call__(self, path, mode=b'r', *args, **kw):
701 def __call__(self, path, mode=b'r', *args, **kw):
698 encoded = self.encode(path)
702 encoded = self.encode(path)
699 if mode not in (b'r', b'rb') and (
703 if mode not in (b'r', b'rb') and (
700 path.startswith(b'data/') or path.startswith(b'meta/')
704 path.startswith(b'data/') or path.startswith(b'meta/')
701 ):
705 ):
702 # do not trigger a fncache load when adding a file that already is
706 # do not trigger a fncache load when adding a file that already is
703 # known to exist.
707 # known to exist.
704 notload = self.fncache.entries is None and self.vfs.exists(encoded)
708 notload = self.fncache.entries is None and self.vfs.exists(encoded)
705 if notload and b'a' in mode and not self.vfs.stat(encoded).st_size:
709 if notload and b'a' in mode and not self.vfs.stat(encoded).st_size:
706 # when appending to an existing file, if the file has size zero,
710 # when appending to an existing file, if the file has size zero,
707 # it should be considered as missing. Such zero-size files are
711 # it should be considered as missing. Such zero-size files are
708 # the result of truncation when a transaction is aborted.
712 # the result of truncation when a transaction is aborted.
709 notload = False
713 notload = False
710 if not notload:
714 if not notload:
711 self.fncache.add(path)
715 self.fncache.add(path)
712 return self.vfs(encoded, mode, *args, **kw)
716 return self.vfs(encoded, mode, *args, **kw)
713
717
714 def join(self, path):
718 def join(self, path):
715 if path:
719 if path:
716 return self.vfs.join(self.encode(path))
720 return self.vfs.join(self.encode(path))
717 else:
721 else:
718 return self.vfs.join(path)
722 return self.vfs.join(path)
719
723
720
724
721 class fncachestore(basicstore):
725 class fncachestore(basicstore):
722 def __init__(self, path, vfstype, dotencode):
726 def __init__(self, path, vfstype, dotencode):
723 if dotencode:
727 if dotencode:
724 encode = _pathencode
728 encode = _pathencode
725 else:
729 else:
726 encode = _plainhybridencode
730 encode = _plainhybridencode
727 self.encode = encode
731 self.encode = encode
728 vfs = vfstype(path + b'/store')
732 vfs = vfstype(path + b'/store')
729 self.path = vfs.base
733 self.path = vfs.base
730 self.pathsep = self.path + b'/'
734 self.pathsep = self.path + b'/'
731 self.createmode = _calcmode(vfs)
735 self.createmode = _calcmode(vfs)
732 vfs.createmode = self.createmode
736 vfs.createmode = self.createmode
733 self.rawvfs = vfs
737 self.rawvfs = vfs
734 fnc = fncache(vfs)
738 fnc = fncache(vfs)
735 self.fncache = fnc
739 self.fncache = fnc
736 self.vfs = _fncachevfs(vfs, fnc, encode)
740 self.vfs = _fncachevfs(vfs, fnc, encode)
737 self.opener = self.vfs
741 self.opener = self.vfs
738
742
739 def join(self, f):
743 def join(self, f):
740 return self.pathsep + self.encode(f)
744 return self.pathsep + self.encode(f)
741
745
742 def getsize(self, path):
746 def getsize(self, path):
743 return self.rawvfs.stat(path).st_size
747 return self.rawvfs.stat(path).st_size
744
748
745 def datafiles(self, matcher=None):
749 def datafiles(self, matcher=None):
746 for f in sorted(self.fncache):
750 for f in sorted(self.fncache):
747 if not _matchtrackedpath(f, matcher):
751 if not _matchtrackedpath(f, matcher):
748 continue
752 continue
749 ef = self.encode(f)
753 ef = self.encode(f)
750 try:
754 try:
751 t = revlog_type(f)
755 t = revlog_type(f)
752 t |= FILEFLAGS_FILELOG
756 t |= FILEFLAGS_FILELOG
753 yield t, f, ef, self.getsize(ef)
757 yield t, f, ef, self.getsize(ef)
754 except OSError as err:
758 except OSError as err:
755 if err.errno != errno.ENOENT:
759 if err.errno != errno.ENOENT:
756 raise
760 raise
757
761
758 def copylist(self):
762 def copylist(self):
759 d = (
763 d = (
760 b'bookmarks',
764 b'bookmarks',
761 b'narrowspec',
765 b'narrowspec',
762 b'data',
766 b'data',
763 b'meta',
767 b'meta',
764 b'dh',
768 b'dh',
765 b'fncache',
769 b'fncache',
766 b'phaseroots',
770 b'phaseroots',
767 b'obsstore',
771 b'obsstore',
768 b'00manifest.d',
772 b'00manifest.d',
769 b'00manifest.i',
773 b'00manifest.i',
770 b'00changelog.d',
774 b'00changelog.d',
771 b'00changelog.i',
775 b'00changelog.i',
772 b'requires',
776 b'requires',
773 )
777 )
774 return [b'requires', b'00changelog.i'] + [b'store/' + f for f in d]
778 return [b'requires', b'00changelog.i'] + [b'store/' + f for f in d]
775
779
776 def write(self, tr):
780 def write(self, tr):
777 self.fncache.write(tr)
781 self.fncache.write(tr)
778
782
779 def invalidatecaches(self):
783 def invalidatecaches(self):
780 self.fncache.entries = None
784 self.fncache.entries = None
781 self.fncache.addls = set()
785 self.fncache.addls = set()
782
786
783 def markremoved(self, fn):
787 def markremoved(self, fn):
784 self.fncache.remove(fn)
788 self.fncache.remove(fn)
785
789
786 def _exists(self, f):
790 def _exists(self, f):
787 ef = self.encode(f)
791 ef = self.encode(f)
788 try:
792 try:
789 self.getsize(ef)
793 self.getsize(ef)
790 return True
794 return True
791 except OSError as err:
795 except OSError as err:
792 if err.errno != errno.ENOENT:
796 if err.errno != errno.ENOENT:
793 raise
797 raise
794 # nonexistent entry
798 # nonexistent entry
795 return False
799 return False
796
800
797 def __contains__(self, path):
801 def __contains__(self, path):
798 '''Checks if the store contains path'''
802 '''Checks if the store contains path'''
799 path = b"/".join((b"data", path))
803 path = b"/".join((b"data", path))
800 # check for files (exact match)
804 # check for files (exact match)
801 e = path + b'.i'
805 e = path + b'.i'
802 if e in self.fncache and self._exists(e):
806 if e in self.fncache and self._exists(e):
803 return True
807 return True
804 # now check for directories (prefix match)
808 # now check for directories (prefix match)
805 if not path.endswith(b'/'):
809 if not path.endswith(b'/'):
806 path += b'/'
810 path += b'/'
807 for e in self.fncache:
811 for e in self.fncache:
808 if e.startswith(path) and self._exists(e):
812 if e.startswith(path) and self._exists(e):
809 return True
813 return True
810 return False
814 return False
@@ -1,1054 +1,1052 b''
1 ===================================
1 ===================================
2 Test the persistent on-disk nodemap
2 Test the persistent on-disk nodemap
3 ===================================
3 ===================================
4
4
5
5
6 #if no-rust
6 #if no-rust
7
7
8 $ cat << EOF >> $HGRCPATH
8 $ cat << EOF >> $HGRCPATH
9 > [format]
9 > [format]
10 > use-persistent-nodemap=yes
10 > use-persistent-nodemap=yes
11 > [devel]
11 > [devel]
12 > persistent-nodemap=yes
12 > persistent-nodemap=yes
13 > EOF
13 > EOF
14
14
15 #endif
15 #endif
16
16
17 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
17 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
18 $ cd test-repo
18 $ cd test-repo
19
19
20 Check handling of the default slow-path value
20 Check handling of the default slow-path value
21
21
22 #if no-pure no-rust
22 #if no-pure no-rust
23
23
24 $ hg id
24 $ hg id
25 abort: accessing `persistent-nodemap` repository without associated fast implementation.
25 abort: accessing `persistent-nodemap` repository without associated fast implementation.
26 (check `hg help config.format.use-persistent-nodemap` for details)
26 (check `hg help config.format.use-persistent-nodemap` for details)
27 [255]
27 [255]
28
28
29 Unlock further check (we are here to test the feature)
29 Unlock further check (we are here to test the feature)
30
30
31 $ cat << EOF >> $HGRCPATH
31 $ cat << EOF >> $HGRCPATH
32 > [storage]
32 > [storage]
33 > # to avoid spamming the test
33 > # to avoid spamming the test
34 > revlog.persistent-nodemap.slow-path=allow
34 > revlog.persistent-nodemap.slow-path=allow
35 > EOF
35 > EOF
36
36
37 #endif
37 #endif
38
38
39 #if rust
39 #if rust
40
40
41 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
41 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
42 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
42 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
43 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
43 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
44 incorrectly used `libc::c_int` (32 bits).
44 incorrectly used `libc::c_int` (32 bits).
45 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
45 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
46
46
47 $ hg log -r 00000000
47 $ hg log -r 00000000
48 changeset: -1:000000000000
48 changeset: -1:000000000000
49 tag: tip
49 tag: tip
50 user:
50 user:
51 date: Thu Jan 01 00:00:00 1970 +0000
51 date: Thu Jan 01 00:00:00 1970 +0000
52
52
53
53
54 #endif
54 #endif
55
55
56
56
57 $ hg debugformat
57 $ hg debugformat
58 format-variant repo
58 format-variant repo
59 fncache: yes
59 fncache: yes
60 dotencode: yes
60 dotencode: yes
61 generaldelta: yes
61 generaldelta: yes
62 share-safe: no
62 share-safe: no
63 sparserevlog: yes
63 sparserevlog: yes
64 persistent-nodemap: yes
64 persistent-nodemap: yes
65 copies-sdc: no
65 copies-sdc: no
66 revlog-v2: no
66 revlog-v2: no
67 plain-cl-delta: yes
67 plain-cl-delta: yes
68 compression: zlib (no-zstd !)
68 compression: zlib (no-zstd !)
69 compression: zstd (zstd !)
69 compression: zstd (zstd !)
70 compression-level: default
70 compression-level: default
71 $ hg debugbuilddag .+5000 --new-file
71 $ hg debugbuilddag .+5000 --new-file
72
72
73 $ hg debugnodemap --metadata
73 $ hg debugnodemap --metadata
74 uid: ???????????????? (glob)
74 uid: ???????????????? (glob)
75 tip-rev: 5000
75 tip-rev: 5000
76 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
76 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
77 data-length: 121088
77 data-length: 121088
78 data-unused: 0
78 data-unused: 0
79 data-unused: 0.000%
79 data-unused: 0.000%
80 $ f --size .hg/store/00changelog.n
80 $ f --size .hg/store/00changelog.n
81 .hg/store/00changelog.n: size=70
81 .hg/store/00changelog.n: size=70
82
82
83 Simple lookup works
83 Simple lookup works
84
84
85 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
85 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
86 $ hg log -r "$ANYNODE" --template '{rev}\n'
86 $ hg log -r "$ANYNODE" --template '{rev}\n'
87 5000
87 5000
88
88
89
89
90 #if rust
90 #if rust
91
91
92 $ f --sha256 .hg/store/00changelog-*.nd
92 $ f --sha256 .hg/store/00changelog-*.nd
93 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
93 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
94
94
95 $ f --sha256 .hg/store/00manifest-*.nd
95 $ f --sha256 .hg/store/00manifest-*.nd
96 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
96 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
97 $ hg debugnodemap --dump-new | f --sha256 --size
97 $ hg debugnodemap --dump-new | f --sha256 --size
98 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
98 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
99 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
99 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
100 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
100 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
101 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
101 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
102 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
102 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
103 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
103 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
104 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
104 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
105 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
105 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
106 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
106 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
107 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
107 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
108 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
108 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
109 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
109 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
110 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
110 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
111 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
111 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
112 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
112 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
113 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
113 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
114 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
114 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
115 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
115 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
116 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
116 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
117
117
118
118
119 #else
119 #else
120
120
121 $ f --sha256 .hg/store/00changelog-*.nd
121 $ f --sha256 .hg/store/00changelog-*.nd
122 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
122 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
123 $ hg debugnodemap --dump-new | f --sha256 --size
123 $ hg debugnodemap --dump-new | f --sha256 --size
124 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
124 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
125 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
125 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
126 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
126 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
127 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
127 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
128 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
128 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
129 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
129 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
130 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
130 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
131 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
131 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
132 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
132 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
133 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
133 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
134 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
134 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
135 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
135 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
136 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
136 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
137 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
137 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
138 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
138 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
139 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
139 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
140 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
140 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
141 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
141 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
142 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
142 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
143
143
144 #endif
144 #endif
145
145
146 $ hg debugnodemap --check
146 $ hg debugnodemap --check
147 revision in index: 5001
147 revision in index: 5001
148 revision in nodemap: 5001
148 revision in nodemap: 5001
149
149
150 add a new commit
150 add a new commit
151
151
152 $ hg up
152 $ hg up
153 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 $ echo foo > foo
154 $ echo foo > foo
155 $ hg add foo
155 $ hg add foo
156
156
157
157
158 Check slow-path config value handling
158 Check slow-path config value handling
159 -------------------------------------
159 -------------------------------------
160
160
161 #if no-pure no-rust
161 #if no-pure no-rust
162
162
163 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
163 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
164 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
164 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
165 falling back to default value: abort
165 falling back to default value: abort
166 abort: accessing `persistent-nodemap` repository without associated fast implementation.
166 abort: accessing `persistent-nodemap` repository without associated fast implementation.
167 (check `hg help config.format.use-persistent-nodemap` for details)
167 (check `hg help config.format.use-persistent-nodemap` for details)
168 [255]
168 [255]
169
169
170 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
170 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
171 warning: accessing `persistent-nodemap` repository without associated fast implementation.
171 warning: accessing `persistent-nodemap` repository without associated fast implementation.
172 (check `hg help config.format.use-persistent-nodemap` for details)
172 (check `hg help config.format.use-persistent-nodemap` for details)
173 changeset: 5000:6b02b8c7b966
173 changeset: 5000:6b02b8c7b966
174 tag: tip
174 tag: tip
175 user: debugbuilddag
175 user: debugbuilddag
176 date: Thu Jan 01 01:23:20 1970 +0000
176 date: Thu Jan 01 01:23:20 1970 +0000
177 summary: r5000
177 summary: r5000
178
178
179 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
179 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
180 abort: accessing `persistent-nodemap` repository without associated fast implementation.
180 abort: accessing `persistent-nodemap` repository without associated fast implementation.
181 (check `hg help config.format.use-persistent-nodemap` for details)
181 (check `hg help config.format.use-persistent-nodemap` for details)
182 [255]
182 [255]
183
183
184 #else
184 #else
185
185
186 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
186 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
187 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
187 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
188 falling back to default value: abort
188 falling back to default value: abort
189 6b02b8c7b966+ tip
189 6b02b8c7b966+ tip
190
190
191 #endif
191 #endif
192
192
193 $ hg ci -m 'foo'
193 $ hg ci -m 'foo'
194
194
195 #if no-pure no-rust
195 #if no-pure no-rust
196 $ hg debugnodemap --metadata
196 $ hg debugnodemap --metadata
197 uid: ???????????????? (glob)
197 uid: ???????????????? (glob)
198 tip-rev: 5001
198 tip-rev: 5001
199 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
199 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
200 data-length: 121088
200 data-length: 121088
201 data-unused: 0
201 data-unused: 0
202 data-unused: 0.000%
202 data-unused: 0.000%
203 #else
203 #else
204 $ hg debugnodemap --metadata
204 $ hg debugnodemap --metadata
205 uid: ???????????????? (glob)
205 uid: ???????????????? (glob)
206 tip-rev: 5001
206 tip-rev: 5001
207 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
207 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
208 data-length: 121344
208 data-length: 121344
209 data-unused: 256
209 data-unused: 256
210 data-unused: 0.211%
210 data-unused: 0.211%
211 #endif
211 #endif
212
212
213 $ f --size .hg/store/00changelog.n
213 $ f --size .hg/store/00changelog.n
214 .hg/store/00changelog.n: size=70
214 .hg/store/00changelog.n: size=70
215
215
216 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
216 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
217
217
218 #if pure
218 #if pure
219 $ f --sha256 .hg/store/00changelog-*.nd --size
219 $ f --sha256 .hg/store/00changelog-*.nd --size
220 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
220 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
221 #endif
221 #endif
222
222
223 #if rust
223 #if rust
224 $ f --sha256 .hg/store/00changelog-*.nd --size
224 $ f --sha256 .hg/store/00changelog-*.nd --size
225 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
225 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
226 #endif
226 #endif
227
227
228 #if no-pure no-rust
228 #if no-pure no-rust
229 $ f --sha256 .hg/store/00changelog-*.nd --size
229 $ f --sha256 .hg/store/00changelog-*.nd --size
230 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
230 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
231 #endif
231 #endif
232
232
233 $ hg debugnodemap --check
233 $ hg debugnodemap --check
234 revision in index: 5002
234 revision in index: 5002
235 revision in nodemap: 5002
235 revision in nodemap: 5002
236
236
237 Test code path without mmap
237 Test code path without mmap
238 ---------------------------
238 ---------------------------
239
239
240 $ echo bar > bar
240 $ echo bar > bar
241 $ hg add bar
241 $ hg add bar
242 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
242 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
243
243
244 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
244 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
245 revision in index: 5003
245 revision in index: 5003
246 revision in nodemap: 5003
246 revision in nodemap: 5003
247 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
247 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
248 revision in index: 5003
248 revision in index: 5003
249 revision in nodemap: 5003
249 revision in nodemap: 5003
250
250
251
251
252 #if pure
252 #if pure
253 $ hg debugnodemap --metadata
253 $ hg debugnodemap --metadata
254 uid: ???????????????? (glob)
254 uid: ???????????????? (glob)
255 tip-rev: 5002
255 tip-rev: 5002
256 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
256 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
257 data-length: 121600
257 data-length: 121600
258 data-unused: 512
258 data-unused: 512
259 data-unused: 0.421%
259 data-unused: 0.421%
260 $ f --sha256 .hg/store/00changelog-*.nd --size
260 $ f --sha256 .hg/store/00changelog-*.nd --size
261 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
261 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
262 #endif
262 #endif
263 #if rust
263 #if rust
264 $ hg debugnodemap --metadata
264 $ hg debugnodemap --metadata
265 uid: ???????????????? (glob)
265 uid: ???????????????? (glob)
266 tip-rev: 5002
266 tip-rev: 5002
267 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
267 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
268 data-length: 121600
268 data-length: 121600
269 data-unused: 512
269 data-unused: 512
270 data-unused: 0.421%
270 data-unused: 0.421%
271 $ f --sha256 .hg/store/00changelog-*.nd --size
271 $ f --sha256 .hg/store/00changelog-*.nd --size
272 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
272 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
273 #endif
273 #endif
274 #if no-pure no-rust
274 #if no-pure no-rust
275 $ hg debugnodemap --metadata
275 $ hg debugnodemap --metadata
276 uid: ???????????????? (glob)
276 uid: ???????????????? (glob)
277 tip-rev: 5002
277 tip-rev: 5002
278 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
278 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
279 data-length: 121088
279 data-length: 121088
280 data-unused: 0
280 data-unused: 0
281 data-unused: 0.000%
281 data-unused: 0.000%
282 $ f --sha256 .hg/store/00changelog-*.nd --size
282 $ f --sha256 .hg/store/00changelog-*.nd --size
283 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
283 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
284 #endif
284 #endif
285
285
286 Test force warming the cache
286 Test force warming the cache
287
287
288 $ rm .hg/store/00changelog.n
288 $ rm .hg/store/00changelog.n
289 $ hg debugnodemap --metadata
289 $ hg debugnodemap --metadata
290 $ hg debugupdatecache
290 $ hg debugupdatecache
291 #if pure
291 #if pure
292 $ hg debugnodemap --metadata
292 $ hg debugnodemap --metadata
293 uid: ???????????????? (glob)
293 uid: ???????????????? (glob)
294 tip-rev: 5002
294 tip-rev: 5002
295 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
295 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
296 data-length: 121088
296 data-length: 121088
297 data-unused: 0
297 data-unused: 0
298 data-unused: 0.000%
298 data-unused: 0.000%
299 #else
299 #else
300 $ hg debugnodemap --metadata
300 $ hg debugnodemap --metadata
301 uid: ???????????????? (glob)
301 uid: ???????????????? (glob)
302 tip-rev: 5002
302 tip-rev: 5002
303 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
303 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
304 data-length: 121088
304 data-length: 121088
305 data-unused: 0
305 data-unused: 0
306 data-unused: 0.000%
306 data-unused: 0.000%
307 #endif
307 #endif
308
308
309 Check out of sync nodemap
309 Check out of sync nodemap
310 =========================
310 =========================
311
311
312 First copy old data on the side.
312 First copy old data on the side.
313
313
314 $ mkdir ../tmp-copies
314 $ mkdir ../tmp-copies
315 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
315 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
316
316
317 Nodemap lagging behind
317 Nodemap lagging behind
318 ----------------------
318 ----------------------
319
319
320 make a new commit
320 make a new commit
321
321
322 $ echo bar2 > bar
322 $ echo bar2 > bar
323 $ hg ci -m 'bar2'
323 $ hg ci -m 'bar2'
324 $ NODE=`hg log -r tip -T '{node}\n'`
324 $ NODE=`hg log -r tip -T '{node}\n'`
325 $ hg log -r "$NODE" -T '{rev}\n'
325 $ hg log -r "$NODE" -T '{rev}\n'
326 5003
326 5003
327
327
328 If the nodemap is lagging behind, it can catch up fine
328 If the nodemap is lagging behind, it can catch up fine
329
329
330 $ hg debugnodemap --metadata
330 $ hg debugnodemap --metadata
331 uid: ???????????????? (glob)
331 uid: ???????????????? (glob)
332 tip-rev: 5003
332 tip-rev: 5003
333 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
333 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
334 data-length: 121344 (pure !)
334 data-length: 121344 (pure !)
335 data-length: 121344 (rust !)
335 data-length: 121344 (rust !)
336 data-length: 121152 (no-rust no-pure !)
336 data-length: 121152 (no-rust no-pure !)
337 data-unused: 192 (pure !)
337 data-unused: 192 (pure !)
338 data-unused: 192 (rust !)
338 data-unused: 192 (rust !)
339 data-unused: 0 (no-rust no-pure !)
339 data-unused: 0 (no-rust no-pure !)
340 data-unused: 0.158% (pure !)
340 data-unused: 0.158% (pure !)
341 data-unused: 0.158% (rust !)
341 data-unused: 0.158% (rust !)
342 data-unused: 0.000% (no-rust no-pure !)
342 data-unused: 0.000% (no-rust no-pure !)
343 $ cp -f ../tmp-copies/* .hg/store/
343 $ cp -f ../tmp-copies/* .hg/store/
344 $ hg debugnodemap --metadata
344 $ hg debugnodemap --metadata
345 uid: ???????????????? (glob)
345 uid: ???????????????? (glob)
346 tip-rev: 5002
346 tip-rev: 5002
347 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
347 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
348 data-length: 121088
348 data-length: 121088
349 data-unused: 0
349 data-unused: 0
350 data-unused: 0.000%
350 data-unused: 0.000%
351 $ hg log -r "$NODE" -T '{rev}\n'
351 $ hg log -r "$NODE" -T '{rev}\n'
352 5003
352 5003
353
353
354 changelog altered
354 changelog altered
355 -----------------
355 -----------------
356
356
357 If the nodemap is not gated behind a requirements, an unaware client can alter
357 If the nodemap is not gated behind a requirements, an unaware client can alter
358 the repository so the revlog used to generate the nodemap is not longer
358 the repository so the revlog used to generate the nodemap is not longer
359 compatible with the persistent nodemap. We need to detect that.
359 compatible with the persistent nodemap. We need to detect that.
360
360
361 $ hg up "$NODE~5"
361 $ hg up "$NODE~5"
362 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
362 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
363 $ echo bar > babar
363 $ echo bar > babar
364 $ hg add babar
364 $ hg add babar
365 $ hg ci -m 'babar'
365 $ hg ci -m 'babar'
366 created new head
366 created new head
367 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
367 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
368 $ hg log -r "$OTHERNODE" -T '{rev}\n'
368 $ hg log -r "$OTHERNODE" -T '{rev}\n'
369 5004
369 5004
370
370
371 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
371 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
372
372
373 the nodemap should detect the changelog have been tampered with and recover.
373 the nodemap should detect the changelog have been tampered with and recover.
374
374
375 $ hg debugnodemap --metadata
375 $ hg debugnodemap --metadata
376 uid: ???????????????? (glob)
376 uid: ???????????????? (glob)
377 tip-rev: 5002
377 tip-rev: 5002
378 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
378 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
379 data-length: 121536 (pure !)
379 data-length: 121536 (pure !)
380 data-length: 121088 (rust !)
380 data-length: 121088 (rust !)
381 data-length: 121088 (no-pure no-rust !)
381 data-length: 121088 (no-pure no-rust !)
382 data-unused: 448 (pure !)
382 data-unused: 448 (pure !)
383 data-unused: 0 (rust !)
383 data-unused: 0 (rust !)
384 data-unused: 0 (no-pure no-rust !)
384 data-unused: 0 (no-pure no-rust !)
385 data-unused: 0.000% (rust !)
385 data-unused: 0.000% (rust !)
386 data-unused: 0.369% (pure !)
386 data-unused: 0.369% (pure !)
387 data-unused: 0.000% (no-pure no-rust !)
387 data-unused: 0.000% (no-pure no-rust !)
388
388
389 $ cp -f ../tmp-copies/* .hg/store/
389 $ cp -f ../tmp-copies/* .hg/store/
390 $ hg debugnodemap --metadata
390 $ hg debugnodemap --metadata
391 uid: ???????????????? (glob)
391 uid: ???????????????? (glob)
392 tip-rev: 5002
392 tip-rev: 5002
393 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
393 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
394 data-length: 121088
394 data-length: 121088
395 data-unused: 0
395 data-unused: 0
396 data-unused: 0.000%
396 data-unused: 0.000%
397 $ hg log -r "$OTHERNODE" -T '{rev}\n'
397 $ hg log -r "$OTHERNODE" -T '{rev}\n'
398 5002
398 5002
399
399
400 missing data file
400 missing data file
401 -----------------
401 -----------------
402
402
403 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
403 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
404 > sed 's/uid: //'`
404 > sed 's/uid: //'`
405 $ FILE=.hg/store/00changelog-"${UUID}".nd
405 $ FILE=.hg/store/00changelog-"${UUID}".nd
406 $ mv $FILE ../tmp-data-file
406 $ mv $FILE ../tmp-data-file
407 $ cp .hg/store/00changelog.n ../tmp-docket
407 $ cp .hg/store/00changelog.n ../tmp-docket
408
408
409 mercurial don't crash
409 mercurial don't crash
410
410
411 $ hg log -r .
411 $ hg log -r .
412 changeset: 5002:b355ef8adce0
412 changeset: 5002:b355ef8adce0
413 tag: tip
413 tag: tip
414 parent: 4998:d918ad6d18d3
414 parent: 4998:d918ad6d18d3
415 user: test
415 user: test
416 date: Thu Jan 01 00:00:00 1970 +0000
416 date: Thu Jan 01 00:00:00 1970 +0000
417 summary: babar
417 summary: babar
418
418
419 $ hg debugnodemap --metadata
419 $ hg debugnodemap --metadata
420
420
421 $ hg debugupdatecache
421 $ hg debugupdatecache
422 $ hg debugnodemap --metadata
422 $ hg debugnodemap --metadata
423 uid: * (glob)
423 uid: * (glob)
424 tip-rev: 5002
424 tip-rev: 5002
425 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
425 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
426 data-length: 121088
426 data-length: 121088
427 data-unused: 0
427 data-unused: 0
428 data-unused: 0.000%
428 data-unused: 0.000%
429 $ mv ../tmp-data-file $FILE
429 $ mv ../tmp-data-file $FILE
430 $ mv ../tmp-docket .hg/store/00changelog.n
430 $ mv ../tmp-docket .hg/store/00changelog.n
431
431
432 Check transaction related property
432 Check transaction related property
433 ==================================
433 ==================================
434
434
435 An up to date nodemap should be available to shell hooks,
435 An up to date nodemap should be available to shell hooks,
436
436
437 $ echo dsljfl > a
437 $ echo dsljfl > a
438 $ hg add a
438 $ hg add a
439 $ hg ci -m a
439 $ hg ci -m a
440 $ hg debugnodemap --metadata
440 $ hg debugnodemap --metadata
441 uid: ???????????????? (glob)
441 uid: ???????????????? (glob)
442 tip-rev: 5003
442 tip-rev: 5003
443 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
443 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
444 data-length: 121088
444 data-length: 121088
445 data-unused: 0
445 data-unused: 0
446 data-unused: 0.000%
446 data-unused: 0.000%
447 $ echo babar2 > babar
447 $ echo babar2 > babar
448 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
448 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
449 uid: ???????????????? (glob)
449 uid: ???????????????? (glob)
450 tip-rev: 5004
450 tip-rev: 5004
451 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
451 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
452 data-length: 121280 (pure !)
452 data-length: 121280 (pure !)
453 data-length: 121280 (rust !)
453 data-length: 121280 (rust !)
454 data-length: 121088 (no-pure no-rust !)
454 data-length: 121088 (no-pure no-rust !)
455 data-unused: 192 (pure !)
455 data-unused: 192 (pure !)
456 data-unused: 192 (rust !)
456 data-unused: 192 (rust !)
457 data-unused: 0 (no-pure no-rust !)
457 data-unused: 0 (no-pure no-rust !)
458 data-unused: 0.158% (pure !)
458 data-unused: 0.158% (pure !)
459 data-unused: 0.158% (rust !)
459 data-unused: 0.158% (rust !)
460 data-unused: 0.000% (no-pure no-rust !)
460 data-unused: 0.000% (no-pure no-rust !)
461 $ hg debugnodemap --metadata
461 $ hg debugnodemap --metadata
462 uid: ???????????????? (glob)
462 uid: ???????????????? (glob)
463 tip-rev: 5004
463 tip-rev: 5004
464 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
464 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
465 data-length: 121280 (pure !)
465 data-length: 121280 (pure !)
466 data-length: 121280 (rust !)
466 data-length: 121280 (rust !)
467 data-length: 121088 (no-pure no-rust !)
467 data-length: 121088 (no-pure no-rust !)
468 data-unused: 192 (pure !)
468 data-unused: 192 (pure !)
469 data-unused: 192 (rust !)
469 data-unused: 192 (rust !)
470 data-unused: 0 (no-pure no-rust !)
470 data-unused: 0 (no-pure no-rust !)
471 data-unused: 0.158% (pure !)
471 data-unused: 0.158% (pure !)
472 data-unused: 0.158% (rust !)
472 data-unused: 0.158% (rust !)
473 data-unused: 0.000% (no-pure no-rust !)
473 data-unused: 0.000% (no-pure no-rust !)
474
474
475 Another process does not see the pending nodemap content during run.
475 Another process does not see the pending nodemap content during run.
476
476
477 $ PATH=$RUNTESTDIR/testlib/:$PATH
477 $ PATH=$RUNTESTDIR/testlib/:$PATH
478 $ echo qpoasp > a
478 $ echo qpoasp > a
479 $ hg ci -m a2 \
479 $ hg ci -m a2 \
480 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
480 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
481 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
481 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
482
482
483 (read the repository while the commit transaction is pending)
483 (read the repository while the commit transaction is pending)
484
484
485 $ wait-on-file 20 sync-txn-pending && \
485 $ wait-on-file 20 sync-txn-pending && \
486 > hg debugnodemap --metadata && \
486 > hg debugnodemap --metadata && \
487 > wait-on-file 20 sync-txn-close sync-repo-read
487 > wait-on-file 20 sync-txn-close sync-repo-read
488 uid: ???????????????? (glob)
488 uid: ???????????????? (glob)
489 tip-rev: 5004
489 tip-rev: 5004
490 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
490 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
491 data-length: 121280 (pure !)
491 data-length: 121280 (pure !)
492 data-length: 121280 (rust !)
492 data-length: 121280 (rust !)
493 data-length: 121088 (no-pure no-rust !)
493 data-length: 121088 (no-pure no-rust !)
494 data-unused: 192 (pure !)
494 data-unused: 192 (pure !)
495 data-unused: 192 (rust !)
495 data-unused: 192 (rust !)
496 data-unused: 0 (no-pure no-rust !)
496 data-unused: 0 (no-pure no-rust !)
497 data-unused: 0.158% (pure !)
497 data-unused: 0.158% (pure !)
498 data-unused: 0.158% (rust !)
498 data-unused: 0.158% (rust !)
499 data-unused: 0.000% (no-pure no-rust !)
499 data-unused: 0.000% (no-pure no-rust !)
500 $ hg debugnodemap --metadata
500 $ hg debugnodemap --metadata
501 uid: ???????????????? (glob)
501 uid: ???????????????? (glob)
502 tip-rev: 5005
502 tip-rev: 5005
503 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
503 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
504 data-length: 121536 (pure !)
504 data-length: 121536 (pure !)
505 data-length: 121536 (rust !)
505 data-length: 121536 (rust !)
506 data-length: 121088 (no-pure no-rust !)
506 data-length: 121088 (no-pure no-rust !)
507 data-unused: 448 (pure !)
507 data-unused: 448 (pure !)
508 data-unused: 448 (rust !)
508 data-unused: 448 (rust !)
509 data-unused: 0 (no-pure no-rust !)
509 data-unused: 0 (no-pure no-rust !)
510 data-unused: 0.369% (pure !)
510 data-unused: 0.369% (pure !)
511 data-unused: 0.369% (rust !)
511 data-unused: 0.369% (rust !)
512 data-unused: 0.000% (no-pure no-rust !)
512 data-unused: 0.000% (no-pure no-rust !)
513
513
514 $ cat output.txt
514 $ cat output.txt
515
515
516 Check that a failing transaction will properly revert the data
516 Check that a failing transaction will properly revert the data
517
517
518 $ echo plakfe > a
518 $ echo plakfe > a
519 $ f --size --sha256 .hg/store/00changelog-*.nd
519 $ f --size --sha256 .hg/store/00changelog-*.nd
520 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
520 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
521 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
521 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
522 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
522 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
523 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
523 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
524 transaction abort!
524 transaction abort!
525 rollback completed
525 rollback completed
526 abort: This is a late abort
526 abort: This is a late abort
527 [255]
527 [255]
528 $ hg debugnodemap --metadata
528 $ hg debugnodemap --metadata
529 uid: ???????????????? (glob)
529 uid: ???????????????? (glob)
530 tip-rev: 5005
530 tip-rev: 5005
531 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
531 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
532 data-length: 121536 (pure !)
532 data-length: 121536 (pure !)
533 data-length: 121536 (rust !)
533 data-length: 121536 (rust !)
534 data-length: 121088 (no-pure no-rust !)
534 data-length: 121088 (no-pure no-rust !)
535 data-unused: 448 (pure !)
535 data-unused: 448 (pure !)
536 data-unused: 448 (rust !)
536 data-unused: 448 (rust !)
537 data-unused: 0 (no-pure no-rust !)
537 data-unused: 0 (no-pure no-rust !)
538 data-unused: 0.369% (pure !)
538 data-unused: 0.369% (pure !)
539 data-unused: 0.369% (rust !)
539 data-unused: 0.369% (rust !)
540 data-unused: 0.000% (no-pure no-rust !)
540 data-unused: 0.000% (no-pure no-rust !)
541 $ f --size --sha256 .hg/store/00changelog-*.nd
541 $ f --size --sha256 .hg/store/00changelog-*.nd
542 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
542 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
543 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
543 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
544 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
544 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
545
545
546 Check that removing content does not confuse the nodemap
546 Check that removing content does not confuse the nodemap
547 --------------------------------------------------------
547 --------------------------------------------------------
548
548
549 removing data with rollback
549 removing data with rollback
550
550
551 $ echo aso > a
551 $ echo aso > a
552 $ hg ci -m a4
552 $ hg ci -m a4
553 $ hg rollback
553 $ hg rollback
554 repository tip rolled back to revision 5005 (undo commit)
554 repository tip rolled back to revision 5005 (undo commit)
555 working directory now based on revision 5005
555 working directory now based on revision 5005
556 $ hg id -r .
556 $ hg id -r .
557 90d5d3ba2fc4 tip
557 90d5d3ba2fc4 tip
558
558
559 roming data with strip
559 roming data with strip
560
560
561 $ echo aso > a
561 $ echo aso > a
562 $ hg ci -m a4
562 $ hg ci -m a4
563 $ hg --config extensions.strip= strip -r . --no-backup
563 $ hg --config extensions.strip= strip -r . --no-backup
564 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
564 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
565 $ hg id -r . --traceback
565 $ hg id -r . --traceback
566 90d5d3ba2fc4 tip
566 90d5d3ba2fc4 tip
567
567
568 Test upgrade / downgrade
568 Test upgrade / downgrade
569 ========================
569 ========================
570
570
571 downgrading
571 downgrading
572
572
573 $ cat << EOF >> .hg/hgrc
573 $ cat << EOF >> .hg/hgrc
574 > [format]
574 > [format]
575 > use-persistent-nodemap=no
575 > use-persistent-nodemap=no
576 > EOF
576 > EOF
577 $ hg debugformat -v
577 $ hg debugformat -v
578 format-variant repo config default
578 format-variant repo config default
579 fncache: yes yes yes
579 fncache: yes yes yes
580 dotencode: yes yes yes
580 dotencode: yes yes yes
581 generaldelta: yes yes yes
581 generaldelta: yes yes yes
582 share-safe: no no no
582 share-safe: no no no
583 sparserevlog: yes yes yes
583 sparserevlog: yes yes yes
584 persistent-nodemap: yes no no
584 persistent-nodemap: yes no no
585 copies-sdc: no no no
585 copies-sdc: no no no
586 revlog-v2: no no no
586 revlog-v2: no no no
587 plain-cl-delta: yes yes yes
587 plain-cl-delta: yes yes yes
588 compression: zlib zlib zlib (no-zstd !)
588 compression: zlib zlib zlib (no-zstd !)
589 compression: zstd zstd zstd (zstd !)
589 compression: zstd zstd zstd (zstd !)
590 compression-level: default default default
590 compression-level: default default default
591 $ hg debugupgraderepo --run --no-backup
591 $ hg debugupgraderepo --run --no-backup
592 upgrade will perform the following actions:
592 upgrade will perform the following actions:
593
593
594 requirements
594 requirements
595 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd !)
595 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd !)
596 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
596 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
597 removed: persistent-nodemap
597 removed: persistent-nodemap
598
598
599 processed revlogs:
599 processed revlogs:
600 - all-filelogs
600 - all-filelogs
601 - changelog
601 - changelog
602 - manifest
602 - manifest
603
603
604 beginning upgrade...
604 beginning upgrade...
605 repository locked and read-only
605 repository locked and read-only
606 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
606 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
607 (it is safe to interrupt this process any time before data migration completes)
607 (it is safe to interrupt this process any time before data migration completes)
608 downgrading repository to not use persistent nodemap feature
608 downgrading repository to not use persistent nodemap feature
609 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
609 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
610 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
610 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
611 00changelog-*.nd (glob)
611 00changelog-*.nd (glob)
612 00manifest-*.nd (glob)
612 00manifest-*.nd (glob)
613 undo.backup.00changelog.n
613 undo.backup.00changelog.n
614 undo.backup.00manifest.n
614 undo.backup.00manifest.n
615 $ hg debugnodemap --metadata
615 $ hg debugnodemap --metadata
616
616
617
617
618 upgrading
618 upgrading
619
619
620 $ cat << EOF >> .hg/hgrc
620 $ cat << EOF >> .hg/hgrc
621 > [format]
621 > [format]
622 > use-persistent-nodemap=yes
622 > use-persistent-nodemap=yes
623 > EOF
623 > EOF
624 $ hg debugformat -v
624 $ hg debugformat -v
625 format-variant repo config default
625 format-variant repo config default
626 fncache: yes yes yes
626 fncache: yes yes yes
627 dotencode: yes yes yes
627 dotencode: yes yes yes
628 generaldelta: yes yes yes
628 generaldelta: yes yes yes
629 share-safe: no no no
629 share-safe: no no no
630 sparserevlog: yes yes yes
630 sparserevlog: yes yes yes
631 persistent-nodemap: no yes no
631 persistent-nodemap: no yes no
632 copies-sdc: no no no
632 copies-sdc: no no no
633 revlog-v2: no no no
633 revlog-v2: no no no
634 plain-cl-delta: yes yes yes
634 plain-cl-delta: yes yes yes
635 compression: zlib zlib zlib (no-zstd !)
635 compression: zlib zlib zlib (no-zstd !)
636 compression: zstd zstd zstd (zstd !)
636 compression: zstd zstd zstd (zstd !)
637 compression-level: default default default
637 compression-level: default default default
638 $ hg debugupgraderepo --run --no-backup
638 $ hg debugupgraderepo --run --no-backup
639 upgrade will perform the following actions:
639 upgrade will perform the following actions:
640
640
641 requirements
641 requirements
642 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd !)
642 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd !)
643 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
643 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
644 added: persistent-nodemap
644 added: persistent-nodemap
645
645
646 persistent-nodemap
646 persistent-nodemap
647 Speedup revision lookup by node id.
647 Speedup revision lookup by node id.
648
648
649 processed revlogs:
649 processed revlogs:
650 - all-filelogs
650 - all-filelogs
651 - changelog
651 - changelog
652 - manifest
652 - manifest
653
653
654 beginning upgrade...
654 beginning upgrade...
655 repository locked and read-only
655 repository locked and read-only
656 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
656 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
657 (it is safe to interrupt this process any time before data migration completes)
657 (it is safe to interrupt this process any time before data migration completes)
658 upgrading repository to use persistent nodemap feature
658 upgrading repository to use persistent nodemap feature
659 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
659 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
660 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
660 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
661 00changelog-*.nd (glob)
661 00changelog-*.nd (glob)
662 00changelog.n
662 00changelog.n
663 00manifest-*.nd (glob)
663 00manifest-*.nd (glob)
664 00manifest.n
664 00manifest.n
665 undo.backup.00changelog.n
665 undo.backup.00changelog.n
666 undo.backup.00manifest.n
666 undo.backup.00manifest.n
667
667
668 $ hg debugnodemap --metadata
668 $ hg debugnodemap --metadata
669 uid: * (glob)
669 uid: * (glob)
670 tip-rev: 5005
670 tip-rev: 5005
671 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
671 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
672 data-length: 121088
672 data-length: 121088
673 data-unused: 0
673 data-unused: 0
674 data-unused: 0.000%
674 data-unused: 0.000%
675
675
676 Running unrelated upgrade
676 Running unrelated upgrade
677
677
678 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
678 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
679 upgrade will perform the following actions:
679 upgrade will perform the following actions:
680
680
681 requirements
681 requirements
682 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (no-zstd !)
682 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (no-zstd !)
683 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
683 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd !)
684
684
685 optimisations: re-delta-all
685 optimisations: re-delta-all
686
686
687 processed revlogs:
687 processed revlogs:
688 - all-filelogs
688 - all-filelogs
689 - changelog
689 - changelog
690 - manifest
690 - manifest
691
691
692 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
692 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
693 00changelog-*.nd (glob)
693 00changelog-*.nd (glob)
694 00changelog.n
694 00changelog.n
695 00manifest-*.nd (glob)
695 00manifest-*.nd (glob)
696 00manifest.n
696 00manifest.n
697
697
698 $ hg debugnodemap --metadata
698 $ hg debugnodemap --metadata
699 uid: * (glob)
699 uid: * (glob)
700 tip-rev: 5005
700 tip-rev: 5005
701 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
701 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
702 data-length: 121088
702 data-length: 121088
703 data-unused: 0
703 data-unused: 0
704 data-unused: 0.000%
704 data-unused: 0.000%
705
705
706 Persistent nodemap and local/streaming clone
706 Persistent nodemap and local/streaming clone
707 ============================================
707 ============================================
708
708
709 $ cd ..
709 $ cd ..
710
710
711 standard clone
711 standard clone
712 --------------
712 --------------
713
713
714 The persistent nodemap should exist after a streaming clone
714 The persistent nodemap should exist after a streaming clone
715
715
716 $ hg clone --pull --quiet -U test-repo standard-clone
716 $ hg clone --pull --quiet -U test-repo standard-clone
717 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
717 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
718 00changelog-*.nd (glob)
718 00changelog-*.nd (glob)
719 00changelog.n
719 00changelog.n
720 00manifest-*.nd (glob)
720 00manifest-*.nd (glob)
721 00manifest.n
721 00manifest.n
722 $ hg -R standard-clone debugnodemap --metadata
722 $ hg -R standard-clone debugnodemap --metadata
723 uid: * (glob)
723 uid: * (glob)
724 tip-rev: 5005
724 tip-rev: 5005
725 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
725 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
726 data-length: 121088
726 data-length: 121088
727 data-unused: 0
727 data-unused: 0
728 data-unused: 0.000%
728 data-unused: 0.000%
729
729
730
730
731 local clone
731 local clone
732 ------------
732 ------------
733
733
734 The persistent nodemap should exist after a streaming clone
734 The persistent nodemap should exist after a streaming clone
735
735
736 $ hg clone -U test-repo local-clone
736 $ hg clone -U test-repo local-clone
737 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
737 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
738 00changelog-*.nd (glob)
738 00changelog-*.nd (glob)
739 00changelog.n
739 00changelog.n
740 00manifest-*.nd (glob)
740 00manifest-*.nd (glob)
741 00manifest.n
741 00manifest.n
742 $ hg -R local-clone debugnodemap --metadata
742 $ hg -R local-clone debugnodemap --metadata
743 uid: * (glob)
743 uid: * (glob)
744 tip-rev: 5005
744 tip-rev: 5005
745 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
745 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
746 data-length: 121088
746 data-length: 121088
747 data-unused: 0
747 data-unused: 0
748 data-unused: 0.000%
748 data-unused: 0.000%
749
749
750 Test various corruption case
750 Test various corruption case
751 ============================
751 ============================
752
752
753 Missing datafile
753 Missing datafile
754 ----------------
754 ----------------
755
755
756 Test behavior with a missing datafile
756 Test behavior with a missing datafile
757
757
758 $ hg clone --quiet --pull test-repo corruption-test-repo
758 $ hg clone --quiet --pull test-repo corruption-test-repo
759 $ ls -1 corruption-test-repo/.hg/store/00changelog*
759 $ ls -1 corruption-test-repo/.hg/store/00changelog*
760 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
760 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
761 corruption-test-repo/.hg/store/00changelog.d
761 corruption-test-repo/.hg/store/00changelog.d
762 corruption-test-repo/.hg/store/00changelog.i
762 corruption-test-repo/.hg/store/00changelog.i
763 corruption-test-repo/.hg/store/00changelog.n
763 corruption-test-repo/.hg/store/00changelog.n
764 $ rm corruption-test-repo/.hg/store/00changelog*.nd
764 $ rm corruption-test-repo/.hg/store/00changelog*.nd
765 $ hg log -R corruption-test-repo -r .
765 $ hg log -R corruption-test-repo -r .
766 changeset: 5005:90d5d3ba2fc4
766 changeset: 5005:90d5d3ba2fc4
767 tag: tip
767 tag: tip
768 user: test
768 user: test
769 date: Thu Jan 01 00:00:00 1970 +0000
769 date: Thu Jan 01 00:00:00 1970 +0000
770 summary: a2
770 summary: a2
771
771
772 $ ls -1 corruption-test-repo/.hg/store/00changelog*
772 $ ls -1 corruption-test-repo/.hg/store/00changelog*
773 corruption-test-repo/.hg/store/00changelog.d
773 corruption-test-repo/.hg/store/00changelog.d
774 corruption-test-repo/.hg/store/00changelog.i
774 corruption-test-repo/.hg/store/00changelog.i
775 corruption-test-repo/.hg/store/00changelog.n
775 corruption-test-repo/.hg/store/00changelog.n
776
776
777 Truncated data file
777 Truncated data file
778 -------------------
778 -------------------
779
779
780 Test behavior with a too short datafile
780 Test behavior with a too short datafile
781
781
782 rebuild the missing data
782 rebuild the missing data
783 $ hg -R corruption-test-repo debugupdatecache
783 $ hg -R corruption-test-repo debugupdatecache
784 $ ls -1 corruption-test-repo/.hg/store/00changelog*
784 $ ls -1 corruption-test-repo/.hg/store/00changelog*
785 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
785 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
786 corruption-test-repo/.hg/store/00changelog.d
786 corruption-test-repo/.hg/store/00changelog.d
787 corruption-test-repo/.hg/store/00changelog.i
787 corruption-test-repo/.hg/store/00changelog.i
788 corruption-test-repo/.hg/store/00changelog.n
788 corruption-test-repo/.hg/store/00changelog.n
789
789
790 truncate the file
790 truncate the file
791
791
792 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
792 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
793 $ f -s $datafilepath
793 $ f -s $datafilepath
794 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
794 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
795 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=none
795 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=none
796 $ mv $datafilepath-tmp $datafilepath
796 $ mv $datafilepath-tmp $datafilepath
797 $ f -s $datafilepath
797 $ f -s $datafilepath
798 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
798 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
799
799
800 Check that Mercurial reaction to this event
800 Check that Mercurial reaction to this event
801
801
802 $ hg -R corruption-test-repo log -r . --traceback
802 $ hg -R corruption-test-repo log -r . --traceback
803 changeset: 5005:90d5d3ba2fc4
803 changeset: 5005:90d5d3ba2fc4
804 tag: tip
804 tag: tip
805 user: test
805 user: test
806 date: Thu Jan 01 00:00:00 1970 +0000
806 date: Thu Jan 01 00:00:00 1970 +0000
807 summary: a2
807 summary: a2
808
808
809
809
810
810
811 stream clone
811 stream clone
812 ============
812 ============
813
813
814 The persistent nodemap should exist after a streaming clone
814 The persistent nodemap should exist after a streaming clone
815
815
816 Simple case
816 Simple case
817 -----------
817 -----------
818
818
819 No race condition
819 No race condition
820
820
821 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
821 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
822 adding [s] 00manifest.n (70 bytes)
822 adding [s] 00manifest.n (70 bytes)
823 adding [s] 00manifest-*.nd (118 KB) (glob)
823 adding [s] 00manifest-*.nd (118 KB) (glob)
824 adding [s] 00changelog.n (70 bytes)
824 adding [s] 00changelog.n (70 bytes)
825 adding [s] 00changelog-*.nd (118 KB) (glob)
825 adding [s] 00changelog-*.nd (118 KB) (glob)
826 adding [s] 00manifest.d (452 KB) (no-zstd !)
826 adding [s] 00manifest.d (452 KB) (no-zstd !)
827 adding [s] 00manifest.d (491 KB) (zstd !)
827 adding [s] 00manifest.d (491 KB) (zstd !)
828 adding [s] 00changelog.d (360 KB) (no-zstd !)
828 adding [s] 00changelog.d (360 KB) (no-zstd !)
829 adding [s] 00changelog.d (368 KB) (zstd !)
829 adding [s] 00changelog.d (368 KB) (zstd !)
830 adding [s] 00manifest.i (313 KB)
830 adding [s] 00manifest.i (313 KB)
831 adding [s] 00changelog.i (313 KB)
831 adding [s] 00changelog.i (313 KB)
832 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
832 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
833 00changelog-*.nd (glob)
833 00changelog-*.nd (glob)
834 00changelog.n
834 00changelog.n
835 00manifest-*.nd (glob)
835 00manifest-*.nd (glob)
836 00manifest.n
836 00manifest.n
837 $ hg -R stream-clone debugnodemap --metadata
837 $ hg -R stream-clone debugnodemap --metadata
838 uid: * (glob)
838 uid: * (glob)
839 tip-rev: 5005
839 tip-rev: 5005
840 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
840 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
841 data-length: 121088
841 data-length: 121088
842 data-unused: 0
842 data-unused: 0
843 data-unused: 0.000%
843 data-unused: 0.000%
844
844
845 new data appened
845 new data appened
846 -----------------
846 -----------------
847
847
848 Other commit happening on the server during the stream clone
848 Other commit happening on the server during the stream clone
849
849
850 setup the step-by-step stream cloning
850 setup the step-by-step stream cloning
851
851
852 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
852 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
853 $ export HG_TEST_STREAM_WALKED_FILE_1
853 $ export HG_TEST_STREAM_WALKED_FILE_1
854 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
854 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
855 $ export HG_TEST_STREAM_WALKED_FILE_2
855 $ export HG_TEST_STREAM_WALKED_FILE_2
856 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
856 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
857 $ export HG_TEST_STREAM_WALKED_FILE_3
857 $ export HG_TEST_STREAM_WALKED_FILE_3
858 $ cat << EOF >> test-repo/.hg/hgrc
858 $ cat << EOF >> test-repo/.hg/hgrc
859 > [extensions]
859 > [extensions]
860 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
860 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
861 > EOF
861 > EOF
862
862
863 Check and record file state beforehand
863 Check and record file state beforehand
864
864
865 $ f --size test-repo/.hg/store/00changelog*
865 $ f --size test-repo/.hg/store/00changelog*
866 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
866 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
867 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
867 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
868 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
868 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
869 test-repo/.hg/store/00changelog.i: size=320384
869 test-repo/.hg/store/00changelog.i: size=320384
870 test-repo/.hg/store/00changelog.n: size=70
870 test-repo/.hg/store/00changelog.n: size=70
871 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
871 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
872 uid: * (glob)
872 uid: * (glob)
873 tip-rev: 5005
873 tip-rev: 5005
874 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
874 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
875 data-length: 121088
875 data-length: 121088
876 data-unused: 0
876 data-unused: 0
877 data-unused: 0.000%
877 data-unused: 0.000%
878
878
879 Prepare a commit
879 Prepare a commit
880
880
881 $ echo foo >> test-repo/foo
881 $ echo foo >> test-repo/foo
882 $ hg -R test-repo/ add test-repo/foo
882 $ hg -R test-repo/ add test-repo/foo
883
883
884 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
884 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
885
885
886 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
886 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
887 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
887 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
888 $ hg -R test-repo/ commit -m foo
888 $ hg -R test-repo/ commit -m foo
889 $ touch $HG_TEST_STREAM_WALKED_FILE_2
889 $ touch $HG_TEST_STREAM_WALKED_FILE_2
890 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
890 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
891 $ cat clone-output
891 $ cat clone-output
892 adding [s] 00manifest.n (70 bytes)
892 adding [s] 00manifest.n (70 bytes)
893 adding [s] 00manifest-*.nd (118 KB) (glob)
893 adding [s] 00manifest-*.nd (118 KB) (glob)
894 adding [s] 00changelog.n (70 bytes)
894 adding [s] 00changelog.n (70 bytes)
895 adding [s] 00changelog-*.nd (118 KB) (glob)
895 adding [s] 00changelog-*.nd (118 KB) (glob)
896 adding [s] 00manifest.d (452 KB) (no-zstd !)
896 adding [s] 00manifest.d (452 KB) (no-zstd !)
897 adding [s] 00manifest.d (491 KB) (zstd !)
897 adding [s] 00manifest.d (491 KB) (zstd !)
898 adding [s] 00changelog.d (360 KB) (no-zstd !)
898 adding [s] 00changelog.d (360 KB) (no-zstd !)
899 adding [s] 00changelog.d (368 KB) (zstd !)
899 adding [s] 00changelog.d (368 KB) (zstd !)
900 adding [s] 00manifest.i (313 KB)
900 adding [s] 00manifest.i (313 KB)
901 adding [s] 00changelog.i (313 KB)
901 adding [s] 00changelog.i (313 KB)
902
902
903 Check the result state
903 Check the result state
904
904
905 $ f --size stream-clone-race-1/.hg/store/00changelog*
905 $ f --size stream-clone-race-1/.hg/store/00changelog*
906 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
906 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
907 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
907 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
908 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
908 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
909 stream-clone-race-1/.hg/store/00changelog.i: size=320384
909 stream-clone-race-1/.hg/store/00changelog.i: size=320384
910 stream-clone-race-1/.hg/store/00changelog.n: size=70
910 stream-clone-race-1/.hg/store/00changelog.n: size=70
911
911
912 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
912 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
913 uid: * (glob)
913 uid: * (glob)
914 tip-rev: 5005
914 tip-rev: 5005
915 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
915 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
916 data-length: 121088
916 data-length: 121088
917 data-unused: 0
917 data-unused: 0
918 data-unused: 0.000%
918 data-unused: 0.000%
919
919
920 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
920 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
921 (ie: the following diff should be empty)
921 (ie: the following diff should be empty)
922
922
923 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
923 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
924
924
925 #if no-rust no-pure
925 #if no-rust no-pure
926 $ diff -u server-metadata.txt client-metadata.txt
926 $ diff -u server-metadata.txt client-metadata.txt
927 --- server-metadata.txt * (glob)
927 --- server-metadata.txt * (glob)
928 +++ client-metadata.txt * (glob)
928 +++ client-metadata.txt * (glob)
929 @@ -1,4 +1,4 @@
929 @@ -1,4 +1,4 @@
930 -uid: * (glob)
930 -uid: * (glob)
931 +uid: * (glob)
931 +uid: * (glob)
932 tip-rev: 5005
932 tip-rev: 5005
933 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
933 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
934 data-length: 121088
934 data-length: 121088
935 [1]
935 [1]
936 #else
936 #else
937 $ diff -u server-metadata.txt client-metadata.txt
937 $ diff -u server-metadata.txt client-metadata.txt
938 #endif
938 #endif
939
939
940
940
941 Clean up after the test.
941 Clean up after the test.
942
942
943 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
943 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
944 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
944 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
945 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
945 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
946
946
947 full regeneration
947 full regeneration
948 -----------------
948 -----------------
949
949
950 A full nodemap is generated
950 A full nodemap is generated
951
951
952 (ideally this test would append enough data to make sure the nodemap data file
952 (ideally this test would append enough data to make sure the nodemap data file
953 get changed, however to make thing simpler we will force the regeneration for
953 get changed, however to make thing simpler we will force the regeneration for
954 this test.
954 this test.
955
955
956 Check the initial state
956 Check the initial state
957
957
958 $ f --size test-repo/.hg/store/00changelog*
958 $ f --size test-repo/.hg/store/00changelog*
959 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
959 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
960 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
960 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
961 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
961 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
962 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
962 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
963 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
963 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
964 test-repo/.hg/store/00changelog.i: size=320448
964 test-repo/.hg/store/00changelog.i: size=320448
965 test-repo/.hg/store/00changelog.n: size=70
965 test-repo/.hg/store/00changelog.n: size=70
966 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
966 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
967 uid: * (glob)
967 uid: * (glob)
968 tip-rev: 5006
968 tip-rev: 5006
969 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
969 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
970 data-length: 121344 (rust !)
970 data-length: 121344 (rust !)
971 data-length: 121344 (pure !)
971 data-length: 121344 (pure !)
972 data-length: 121152 (no-rust no-pure !)
972 data-length: 121152 (no-rust no-pure !)
973 data-unused: 192 (rust !)
973 data-unused: 192 (rust !)
974 data-unused: 192 (pure !)
974 data-unused: 192 (pure !)
975 data-unused: 0 (no-rust no-pure !)
975 data-unused: 0 (no-rust no-pure !)
976 data-unused: 0.158% (rust !)
976 data-unused: 0.158% (rust !)
977 data-unused: 0.158% (pure !)
977 data-unused: 0.158% (pure !)
978 data-unused: 0.000% (no-rust no-pure !)
978 data-unused: 0.000% (no-rust no-pure !)
979
979
980 Performe the mix of clone and full refresh of the nodemap, so that the files
980 Performe the mix of clone and full refresh of the nodemap, so that the files
981 (and filenames) are different between listing time and actual transfer time.
981 (and filenames) are different between listing time and actual transfer time.
982
982
983 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
983 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
984 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
984 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
985 $ rm test-repo/.hg/store/00changelog.n
985 $ rm test-repo/.hg/store/00changelog.n
986 $ rm test-repo/.hg/store/00changelog-*.nd
986 $ rm test-repo/.hg/store/00changelog-*.nd
987 $ hg -R test-repo/ debugupdatecache
987 $ hg -R test-repo/ debugupdatecache
988 $ touch $HG_TEST_STREAM_WALKED_FILE_2
988 $ touch $HG_TEST_STREAM_WALKED_FILE_2
989 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
989 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
990 $ cat clone-output-2
990 $ cat clone-output-2
991 adding [s] undo.backup.00manifest.n (70 bytes) (known-bad-output !)
992 adding [s] undo.backup.00changelog.n (70 bytes) (known-bad-output !)
993 adding [s] 00manifest.n (70 bytes)
991 adding [s] 00manifest.n (70 bytes)
994 adding [s] 00manifest-*.nd (118 KB) (glob)
992 adding [s] 00manifest-*.nd (118 KB) (glob)
995 adding [s] 00changelog.n (70 bytes)
993 adding [s] 00changelog.n (70 bytes)
996 adding [s] 00changelog-*.nd (118 KB) (glob)
994 adding [s] 00changelog-*.nd (118 KB) (glob)
997 adding [s] 00manifest.d (492 KB) (zstd !)
995 adding [s] 00manifest.d (492 KB) (zstd !)
998 adding [s] 00manifest.d (452 KB) (no-zstd !)
996 adding [s] 00manifest.d (452 KB) (no-zstd !)
999 adding [s] 00changelog.d (360 KB) (no-zstd !)
997 adding [s] 00changelog.d (360 KB) (no-zstd !)
1000 adding [s] 00changelog.d (368 KB) (zstd !)
998 adding [s] 00changelog.d (368 KB) (zstd !)
1001 adding [s] 00manifest.i (313 KB)
999 adding [s] 00manifest.i (313 KB)
1002 adding [s] 00changelog.i (313 KB)
1000 adding [s] 00changelog.i (313 KB)
1003
1001
1004 Check the result.
1002 Check the result.
1005
1003
1006 $ f --size stream-clone-race-2/.hg/store/00changelog*
1004 $ f --size stream-clone-race-2/.hg/store/00changelog*
1007 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1005 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1008 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1006 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1009 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1007 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1010 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1008 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1011 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1009 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1012 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1010 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1013 stream-clone-race-2/.hg/store/00changelog.n: size=70
1011 stream-clone-race-2/.hg/store/00changelog.n: size=70
1014
1012
1015 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1013 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1016 uid: * (glob)
1014 uid: * (glob)
1017 tip-rev: 5006
1015 tip-rev: 5006
1018 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1016 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1019 data-length: 121344 (rust !)
1017 data-length: 121344 (rust !)
1020 data-unused: 192 (rust !)
1018 data-unused: 192 (rust !)
1021 data-unused: 0.158% (rust !)
1019 data-unused: 0.158% (rust !)
1022 data-length: 121152 (no-rust no-pure !)
1020 data-length: 121152 (no-rust no-pure !)
1023 data-unused: 0 (no-rust no-pure !)
1021 data-unused: 0 (no-rust no-pure !)
1024 data-unused: 0.000% (no-rust no-pure !)
1022 data-unused: 0.000% (no-rust no-pure !)
1025 data-length: 121344 (pure !)
1023 data-length: 121344 (pure !)
1026 data-unused: 192 (pure !)
1024 data-unused: 192 (pure !)
1027 data-unused: 0.158% (pure !)
1025 data-unused: 0.158% (pure !)
1028
1026
1029 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1027 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1030 (ie: the following diff should be empty)
1028 (ie: the following diff should be empty)
1031
1029
1032 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1030 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1033
1031
1034 #if no-rust no-pure
1032 #if no-rust no-pure
1035 $ diff -u server-metadata-2.txt client-metadata-2.txt
1033 $ diff -u server-metadata-2.txt client-metadata-2.txt
1036 --- server-metadata-2.txt * (glob)
1034 --- server-metadata-2.txt * (glob)
1037 +++ client-metadata-2.txt * (glob)
1035 +++ client-metadata-2.txt * (glob)
1038 @@ -1,4 +1,4 @@
1036 @@ -1,4 +1,4 @@
1039 -uid: * (glob)
1037 -uid: * (glob)
1040 +uid: * (glob)
1038 +uid: * (glob)
1041 tip-rev: 5006
1039 tip-rev: 5006
1042 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1040 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1043 data-length: 121152
1041 data-length: 121152
1044 [1]
1042 [1]
1045 #else
1043 #else
1046 $ diff -u server-metadata-2.txt client-metadata-2.txt
1044 $ diff -u server-metadata-2.txt client-metadata-2.txt
1047 #endif
1045 #endif
1048
1046
1049 Clean up after the test
1047 Clean up after the test
1050
1048
1051 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1049 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1052 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1050 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1053 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1051 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1054
1052
General Comments 0
You need to be logged in to leave comments. Login now