##// END OF EJS Templates
nodemap: add a new mode value, "strict"...
marmoute -
r45293:6b01799e default
parent child Browse files
Show More
@@ -1,643 +1,645 b''
1 # nodemap.py - nodemap related code and utilities
1 # nodemap.py - nodemap related code and utilities
2 #
2 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import struct
14 import struct
15
15
16 from ..i18n import _
16 from ..i18n import _
17
17
18 from .. import (
18 from .. import (
19 error,
19 error,
20 node as nodemod,
20 node as nodemod,
21 util,
21 util,
22 )
22 )
23
23
24
24
25 class NodeMap(dict):
25 class NodeMap(dict):
26 def __missing__(self, x):
26 def __missing__(self, x):
27 raise error.RevlogError(b'unknown node: %s' % x)
27 raise error.RevlogError(b'unknown node: %s' % x)
28
28
29
29
30 def persisted_data(revlog):
30 def persisted_data(revlog):
31 """read the nodemap for a revlog from disk"""
31 """read the nodemap for a revlog from disk"""
32 if revlog.nodemap_file is None:
32 if revlog.nodemap_file is None:
33 return None
33 return None
34 pdata = revlog.opener.tryread(revlog.nodemap_file)
34 pdata = revlog.opener.tryread(revlog.nodemap_file)
35 if not pdata:
35 if not pdata:
36 return None
36 return None
37 offset = 0
37 offset = 0
38 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
38 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
39 if version != ONDISK_VERSION:
39 if version != ONDISK_VERSION:
40 return None
40 return None
41 offset += S_VERSION.size
41 offset += S_VERSION.size
42 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
42 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
43 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
43 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
44 offset += S_HEADER.size
44 offset += S_HEADER.size
45 docket = NodeMapDocket(pdata[offset : offset + uid_size])
45 docket = NodeMapDocket(pdata[offset : offset + uid_size])
46 offset += uid_size
46 offset += uid_size
47 docket.tip_rev = tip_rev
47 docket.tip_rev = tip_rev
48 docket.tip_node = pdata[offset : offset + tip_node_size]
48 docket.tip_node = pdata[offset : offset + tip_node_size]
49 docket.data_length = data_length
49 docket.data_length = data_length
50 docket.data_unused = data_unused
50 docket.data_unused = data_unused
51
51
52 filename = _rawdata_filepath(revlog, docket)
52 filename = _rawdata_filepath(revlog, docket)
53 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
53 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
54 try:
54 try:
55 with revlog.opener(filename) as fd:
55 with revlog.opener(filename) as fd:
56 if use_mmap:
56 if use_mmap:
57 data = util.buffer(util.mmapread(fd, data_length))
57 data = util.buffer(util.mmapread(fd, data_length))
58 else:
58 else:
59 data = fd.read(data_length)
59 data = fd.read(data_length)
60 except OSError as e:
60 except OSError as e:
61 if e.errno != errno.ENOENT:
61 if e.errno != errno.ENOENT:
62 raise
62 raise
63 if len(data) < data_length:
63 if len(data) < data_length:
64 return None
64 return None
65 return docket, data
65 return docket, data
66
66
67
67
68 def setup_persistent_nodemap(tr, revlog):
68 def setup_persistent_nodemap(tr, revlog):
69 """Install whatever is needed transaction side to persist a nodemap on disk
69 """Install whatever is needed transaction side to persist a nodemap on disk
70
70
71 (only actually persist the nodemap if this is relevant for this revlog)
71 (only actually persist the nodemap if this is relevant for this revlog)
72 """
72 """
73 if revlog._inline:
73 if revlog._inline:
74 return # inlined revlog are too small for this to be relevant
74 return # inlined revlog are too small for this to be relevant
75 if revlog.nodemap_file is None:
75 if revlog.nodemap_file is None:
76 return # we do not use persistent_nodemap on this revlog
76 return # we do not use persistent_nodemap on this revlog
77
77
78 # we need to happen after the changelog finalization, in that use "cl-"
78 # we need to happen after the changelog finalization, in that use "cl-"
79 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog.nodemap_file
79 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog.nodemap_file
80 if tr.hasfinalize(callback_id):
80 if tr.hasfinalize(callback_id):
81 return # no need to register again
81 return # no need to register again
82 tr.addpending(
82 tr.addpending(
83 callback_id, lambda tr: _persist_nodemap(tr, revlog, pending=True)
83 callback_id, lambda tr: _persist_nodemap(tr, revlog, pending=True)
84 )
84 )
85 tr.addfinalize(callback_id, lambda tr: _persist_nodemap(tr, revlog))
85 tr.addfinalize(callback_id, lambda tr: _persist_nodemap(tr, revlog))
86
86
87
87
88 class _NoTransaction(object):
88 class _NoTransaction(object):
89 """transaction like object to update the nodemap outside a transaction
89 """transaction like object to update the nodemap outside a transaction
90 """
90 """
91
91
92 def __init__(self):
92 def __init__(self):
93 self._postclose = {}
93 self._postclose = {}
94
94
95 def addpostclose(self, callback_id, callback_func):
95 def addpostclose(self, callback_id, callback_func):
96 self._postclose[callback_id] = callback_func
96 self._postclose[callback_id] = callback_func
97
97
98 def registertmp(self, *args, **kwargs):
98 def registertmp(self, *args, **kwargs):
99 pass
99 pass
100
100
101 def addbackup(self, *args, **kwargs):
101 def addbackup(self, *args, **kwargs):
102 pass
102 pass
103
103
104 def add(self, *args, **kwargs):
104 def add(self, *args, **kwargs):
105 pass
105 pass
106
106
107 def addabort(self, *args, **kwargs):
107 def addabort(self, *args, **kwargs):
108 pass
108 pass
109
109
110 def _report(self, *args):
110 def _report(self, *args):
111 pass
111 pass
112
112
113
113
114 def update_persistent_nodemap(revlog):
114 def update_persistent_nodemap(revlog):
115 """update the persistent nodemap right now
115 """update the persistent nodemap right now
116
116
117 To be used for updating the nodemap on disk outside of a normal transaction
117 To be used for updating the nodemap on disk outside of a normal transaction
118 setup (eg, `debugupdatecache`).
118 setup (eg, `debugupdatecache`).
119 """
119 """
120 if revlog._inline:
120 if revlog._inline:
121 return # inlined revlog are too small for this to be relevant
121 return # inlined revlog are too small for this to be relevant
122 if revlog.nodemap_file is None:
122 if revlog.nodemap_file is None:
123 return # we do not use persistent_nodemap on this revlog
123 return # we do not use persistent_nodemap on this revlog
124
124
125 notr = _NoTransaction()
125 notr = _NoTransaction()
126 _persist_nodemap(notr, revlog)
126 _persist_nodemap(notr, revlog)
127 for k in sorted(notr._postclose):
127 for k in sorted(notr._postclose):
128 notr._postclose[k](None)
128 notr._postclose[k](None)
129
129
130
130
131 def _persist_nodemap(tr, revlog, pending=False):
131 def _persist_nodemap(tr, revlog, pending=False):
132 """Write nodemap data on disk for a given revlog
132 """Write nodemap data on disk for a given revlog
133 """
133 """
134 if getattr(revlog, 'filteredrevs', ()):
134 if getattr(revlog, 'filteredrevs', ()):
135 raise error.ProgrammingError(
135 raise error.ProgrammingError(
136 "cannot persist nodemap of a filtered changelog"
136 "cannot persist nodemap of a filtered changelog"
137 )
137 )
138 if revlog.nodemap_file is None:
138 if revlog.nodemap_file is None:
139 msg = "calling persist nodemap on a revlog without the feature enableb"
139 msg = "calling persist nodemap on a revlog without the feature enableb"
140 raise error.ProgrammingError(msg)
140 raise error.ProgrammingError(msg)
141
141
142 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
142 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
143 ondisk_docket = revlog._nodemap_docket
143 ondisk_docket = revlog._nodemap_docket
144 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
144 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
145 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
145 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
146 mode = revlog.opener.options.get(b"exp-persistent-nodemap.mode")
146 mode = revlog.opener.options.get(b"exp-persistent-nodemap.mode")
147 if not can_incremental:
147 if not can_incremental:
148 msg = _(b"persistent nodemap in strict mode without efficient method")
148 msg = _(b"persistent nodemap in strict mode without efficient method")
149 if mode == b'warn':
149 if mode == b'warn':
150 tr._report(b"%s\n" % msg)
150 tr._report(b"%s\n" % msg)
151 elif mode == b'strict':
152 raise error.Abort(msg)
151
153
152 data = None
154 data = None
153 # first attemp an incremental update of the data
155 # first attemp an incremental update of the data
154 if can_incremental and ondisk_docket is not None:
156 if can_incremental and ondisk_docket is not None:
155 target_docket = revlog._nodemap_docket.copy()
157 target_docket = revlog._nodemap_docket.copy()
156 (
158 (
157 src_docket,
159 src_docket,
158 data_changed_count,
160 data_changed_count,
159 data,
161 data,
160 ) = revlog.index.nodemap_data_incremental()
162 ) = revlog.index.nodemap_data_incremental()
161 new_length = target_docket.data_length + len(data)
163 new_length = target_docket.data_length + len(data)
162 new_unused = target_docket.data_unused + data_changed_count
164 new_unused = target_docket.data_unused + data_changed_count
163 if src_docket != target_docket:
165 if src_docket != target_docket:
164 data = None
166 data = None
165 elif new_length <= (new_unused * 10): # under 10% of unused data
167 elif new_length <= (new_unused * 10): # under 10% of unused data
166 data = None
168 data = None
167 else:
169 else:
168 datafile = _rawdata_filepath(revlog, target_docket)
170 datafile = _rawdata_filepath(revlog, target_docket)
169 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
171 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
170 # store vfs
172 # store vfs
171 tr.add(datafile, target_docket.data_length)
173 tr.add(datafile, target_docket.data_length)
172 with revlog.opener(datafile, b'r+') as fd:
174 with revlog.opener(datafile, b'r+') as fd:
173 fd.seek(target_docket.data_length)
175 fd.seek(target_docket.data_length)
174 fd.write(data)
176 fd.write(data)
175 if feed_data:
177 if feed_data:
176 if use_mmap:
178 if use_mmap:
177 fd.seek(0)
179 fd.seek(0)
178 new_data = fd.read(new_length)
180 new_data = fd.read(new_length)
179 else:
181 else:
180 fd.flush()
182 fd.flush()
181 new_data = util.buffer(util.mmapread(fd, new_length))
183 new_data = util.buffer(util.mmapread(fd, new_length))
182 target_docket.data_length = new_length
184 target_docket.data_length = new_length
183 target_docket.data_unused = new_unused
185 target_docket.data_unused = new_unused
184
186
185 if data is None:
187 if data is None:
186 # otherwise fallback to a full new export
188 # otherwise fallback to a full new export
187 target_docket = NodeMapDocket()
189 target_docket = NodeMapDocket()
188 datafile = _rawdata_filepath(revlog, target_docket)
190 datafile = _rawdata_filepath(revlog, target_docket)
189 if util.safehasattr(revlog.index, "nodemap_data_all"):
191 if util.safehasattr(revlog.index, "nodemap_data_all"):
190 data = revlog.index.nodemap_data_all()
192 data = revlog.index.nodemap_data_all()
191 else:
193 else:
192 data = persistent_data(revlog.index)
194 data = persistent_data(revlog.index)
193 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
195 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
194 # store vfs
196 # store vfs
195
197
196 tryunlink = revlog.opener.tryunlink
198 tryunlink = revlog.opener.tryunlink
197
199
198 def abortck(tr):
200 def abortck(tr):
199 tryunlink(datafile)
201 tryunlink(datafile)
200
202
201 callback_id = b"delete-%s" % datafile
203 callback_id = b"delete-%s" % datafile
202
204
203 # some flavor of the transaction abort does not cleanup new file, it
205 # some flavor of the transaction abort does not cleanup new file, it
204 # simply empty them.
206 # simply empty them.
205 tr.addabort(callback_id, abortck)
207 tr.addabort(callback_id, abortck)
206 with revlog.opener(datafile, b'w+') as fd:
208 with revlog.opener(datafile, b'w+') as fd:
207 fd.write(data)
209 fd.write(data)
208 if feed_data:
210 if feed_data:
209 if use_mmap:
211 if use_mmap:
210 new_data = data
212 new_data = data
211 else:
213 else:
212 fd.flush()
214 fd.flush()
213 new_data = util.buffer(util.mmapread(fd, len(data)))
215 new_data = util.buffer(util.mmapread(fd, len(data)))
214 target_docket.data_length = len(data)
216 target_docket.data_length = len(data)
215 target_docket.tip_rev = revlog.tiprev()
217 target_docket.tip_rev = revlog.tiprev()
216 target_docket.tip_node = revlog.node(target_docket.tip_rev)
218 target_docket.tip_node = revlog.node(target_docket.tip_rev)
217 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
219 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
218 # store vfs
220 # store vfs
219 file_path = revlog.nodemap_file
221 file_path = revlog.nodemap_file
220 if pending:
222 if pending:
221 file_path += b'.a'
223 file_path += b'.a'
222 tr.registertmp(file_path)
224 tr.registertmp(file_path)
223 else:
225 else:
224 tr.addbackup(file_path)
226 tr.addbackup(file_path)
225
227
226 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
228 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
227 fp.write(target_docket.serialize())
229 fp.write(target_docket.serialize())
228 revlog._nodemap_docket = target_docket
230 revlog._nodemap_docket = target_docket
229 if feed_data:
231 if feed_data:
230 revlog.index.update_nodemap_data(target_docket, new_data)
232 revlog.index.update_nodemap_data(target_docket, new_data)
231
233
232 # search for old index file in all cases, some older process might have
234 # search for old index file in all cases, some older process might have
233 # left one behind.
235 # left one behind.
234 olds = _other_rawdata_filepath(revlog, target_docket)
236 olds = _other_rawdata_filepath(revlog, target_docket)
235 if olds:
237 if olds:
236 realvfs = getattr(revlog, '_realopener', revlog.opener)
238 realvfs = getattr(revlog, '_realopener', revlog.opener)
237
239
238 def cleanup(tr):
240 def cleanup(tr):
239 for oldfile in olds:
241 for oldfile in olds:
240 realvfs.tryunlink(oldfile)
242 realvfs.tryunlink(oldfile)
241
243
242 callback_id = b"revlog-cleanup-nodemap-%s" % revlog.nodemap_file
244 callback_id = b"revlog-cleanup-nodemap-%s" % revlog.nodemap_file
243 tr.addpostclose(callback_id, cleanup)
245 tr.addpostclose(callback_id, cleanup)
244
246
245
247
246 ### Nodemap docket file
248 ### Nodemap docket file
247 #
249 #
248 # The nodemap data are stored on disk using 2 files:
250 # The nodemap data are stored on disk using 2 files:
249 #
251 #
250 # * a raw data files containing a persistent nodemap
252 # * a raw data files containing a persistent nodemap
251 # (see `Nodemap Trie` section)
253 # (see `Nodemap Trie` section)
252 #
254 #
253 # * a small "docket" file containing medatadata
255 # * a small "docket" file containing medatadata
254 #
256 #
255 # While the nodemap data can be multiple tens of megabytes, the "docket" is
257 # While the nodemap data can be multiple tens of megabytes, the "docket" is
256 # small, it is easy to update it automatically or to duplicated its content
258 # small, it is easy to update it automatically or to duplicated its content
257 # during a transaction.
259 # during a transaction.
258 #
260 #
259 # Multiple raw data can exist at the same time (The currently valid one and a
261 # Multiple raw data can exist at the same time (The currently valid one and a
260 # new one beind used by an in progress transaction). To accomodate this, the
262 # new one beind used by an in progress transaction). To accomodate this, the
261 # filename hosting the raw data has a variable parts. The exact filename is
263 # filename hosting the raw data has a variable parts. The exact filename is
262 # specified inside the "docket" file.
264 # specified inside the "docket" file.
263 #
265 #
264 # The docket file contains information to find, qualify and validate the raw
266 # The docket file contains information to find, qualify and validate the raw
265 # data. Its content is currently very light, but it will expand as the on disk
267 # data. Its content is currently very light, but it will expand as the on disk
266 # nodemap gains the necessary features to be used in production.
268 # nodemap gains the necessary features to be used in production.
267
269
268 # version 0 is experimental, no BC garantee, do no use outside of tests.
270 # version 0 is experimental, no BC garantee, do no use outside of tests.
269 ONDISK_VERSION = 0
271 ONDISK_VERSION = 0
270 S_VERSION = struct.Struct(">B")
272 S_VERSION = struct.Struct(">B")
271 S_HEADER = struct.Struct(">BQQQQ")
273 S_HEADER = struct.Struct(">BQQQQ")
272
274
273 ID_SIZE = 8
275 ID_SIZE = 8
274
276
275
277
276 def _make_uid():
278 def _make_uid():
277 """return a new unique identifier.
279 """return a new unique identifier.
278
280
279 The identifier is random and composed of ascii characters."""
281 The identifier is random and composed of ascii characters."""
280 return nodemod.hex(os.urandom(ID_SIZE))
282 return nodemod.hex(os.urandom(ID_SIZE))
281
283
282
284
283 class NodeMapDocket(object):
285 class NodeMapDocket(object):
284 """metadata associated with persistent nodemap data
286 """metadata associated with persistent nodemap data
285
287
286 The persistent data may come from disk or be on their way to disk.
288 The persistent data may come from disk or be on their way to disk.
287 """
289 """
288
290
289 def __init__(self, uid=None):
291 def __init__(self, uid=None):
290 if uid is None:
292 if uid is None:
291 uid = _make_uid()
293 uid = _make_uid()
292 # a unique identifier for the data file:
294 # a unique identifier for the data file:
293 # - When new data are appended, it is preserved.
295 # - When new data are appended, it is preserved.
294 # - When a new data file is created, a new identifier is generated.
296 # - When a new data file is created, a new identifier is generated.
295 self.uid = uid
297 self.uid = uid
296 # the tipmost revision stored in the data file. This revision and all
298 # the tipmost revision stored in the data file. This revision and all
297 # revision before it are expected to be encoded in the data file.
299 # revision before it are expected to be encoded in the data file.
298 self.tip_rev = None
300 self.tip_rev = None
299 # the node of that tipmost revision, if it mismatch the current index
301 # the node of that tipmost revision, if it mismatch the current index
300 # data the docket is not valid for the current index and should be
302 # data the docket is not valid for the current index and should be
301 # discarded.
303 # discarded.
302 #
304 #
303 # note: this method is not perfect as some destructive operation could
305 # note: this method is not perfect as some destructive operation could
304 # preserve the same tip_rev + tip_node while altering lower revision.
306 # preserve the same tip_rev + tip_node while altering lower revision.
305 # However this multiple other caches have the same vulnerability (eg:
307 # However this multiple other caches have the same vulnerability (eg:
306 # brancmap cache).
308 # brancmap cache).
307 self.tip_node = None
309 self.tip_node = None
308 # the size (in bytes) of the persisted data to encode the nodemap valid
310 # the size (in bytes) of the persisted data to encode the nodemap valid
309 # for `tip_rev`.
311 # for `tip_rev`.
310 # - data file shorter than this are corrupted,
312 # - data file shorter than this are corrupted,
311 # - any extra data should be ignored.
313 # - any extra data should be ignored.
312 self.data_length = None
314 self.data_length = None
313 # the amount (in bytes) of "dead" data, still in the data file but no
315 # the amount (in bytes) of "dead" data, still in the data file but no
314 # longer used for the nodemap.
316 # longer used for the nodemap.
315 self.data_unused = 0
317 self.data_unused = 0
316
318
317 def copy(self):
319 def copy(self):
318 new = NodeMapDocket(uid=self.uid)
320 new = NodeMapDocket(uid=self.uid)
319 new.tip_rev = self.tip_rev
321 new.tip_rev = self.tip_rev
320 new.tip_node = self.tip_node
322 new.tip_node = self.tip_node
321 new.data_length = self.data_length
323 new.data_length = self.data_length
322 new.data_unused = self.data_unused
324 new.data_unused = self.data_unused
323 return new
325 return new
324
326
325 def __cmp__(self, other):
327 def __cmp__(self, other):
326 if self.uid < other.uid:
328 if self.uid < other.uid:
327 return -1
329 return -1
328 if self.uid > other.uid:
330 if self.uid > other.uid:
329 return 1
331 return 1
330 elif self.data_length < other.data_length:
332 elif self.data_length < other.data_length:
331 return -1
333 return -1
332 elif self.data_length > other.data_length:
334 elif self.data_length > other.data_length:
333 return 1
335 return 1
334 return 0
336 return 0
335
337
336 def __eq__(self, other):
338 def __eq__(self, other):
337 return self.uid == other.uid and self.data_length == other.data_length
339 return self.uid == other.uid and self.data_length == other.data_length
338
340
339 def serialize(self):
341 def serialize(self):
340 """return serialized bytes for a docket using the passed uid"""
342 """return serialized bytes for a docket using the passed uid"""
341 data = []
343 data = []
342 data.append(S_VERSION.pack(ONDISK_VERSION))
344 data.append(S_VERSION.pack(ONDISK_VERSION))
343 headers = (
345 headers = (
344 len(self.uid),
346 len(self.uid),
345 self.tip_rev,
347 self.tip_rev,
346 self.data_length,
348 self.data_length,
347 self.data_unused,
349 self.data_unused,
348 len(self.tip_node),
350 len(self.tip_node),
349 )
351 )
350 data.append(S_HEADER.pack(*headers))
352 data.append(S_HEADER.pack(*headers))
351 data.append(self.uid)
353 data.append(self.uid)
352 data.append(self.tip_node)
354 data.append(self.tip_node)
353 return b''.join(data)
355 return b''.join(data)
354
356
355
357
356 def _rawdata_filepath(revlog, docket):
358 def _rawdata_filepath(revlog, docket):
357 """The (vfs relative) nodemap's rawdata file for a given uid"""
359 """The (vfs relative) nodemap's rawdata file for a given uid"""
358 if revlog.nodemap_file.endswith(b'.n.a'):
360 if revlog.nodemap_file.endswith(b'.n.a'):
359 prefix = revlog.nodemap_file[:-4]
361 prefix = revlog.nodemap_file[:-4]
360 else:
362 else:
361 prefix = revlog.nodemap_file[:-2]
363 prefix = revlog.nodemap_file[:-2]
362 return b"%s-%s.nd" % (prefix, docket.uid)
364 return b"%s-%s.nd" % (prefix, docket.uid)
363
365
364
366
365 def _other_rawdata_filepath(revlog, docket):
367 def _other_rawdata_filepath(revlog, docket):
366 prefix = revlog.nodemap_file[:-2]
368 prefix = revlog.nodemap_file[:-2]
367 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
369 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
368 new_file_path = _rawdata_filepath(revlog, docket)
370 new_file_path = _rawdata_filepath(revlog, docket)
369 new_file_name = revlog.opener.basename(new_file_path)
371 new_file_name = revlog.opener.basename(new_file_path)
370 dirpath = revlog.opener.dirname(new_file_path)
372 dirpath = revlog.opener.dirname(new_file_path)
371 others = []
373 others = []
372 for f in revlog.opener.listdir(dirpath):
374 for f in revlog.opener.listdir(dirpath):
373 if pattern.match(f) and f != new_file_name:
375 if pattern.match(f) and f != new_file_name:
374 others.append(f)
376 others.append(f)
375 return others
377 return others
376
378
377
379
378 ### Nodemap Trie
380 ### Nodemap Trie
379 #
381 #
380 # This is a simple reference implementation to compute and persist a nodemap
382 # This is a simple reference implementation to compute and persist a nodemap
381 # trie. This reference implementation is write only. The python version of this
383 # trie. This reference implementation is write only. The python version of this
382 # is not expected to be actually used, since it wont provide performance
384 # is not expected to be actually used, since it wont provide performance
383 # improvement over existing non-persistent C implementation.
385 # improvement over existing non-persistent C implementation.
384 #
386 #
385 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
387 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
386 # revision can be adressed using its node shortest prefix.
388 # revision can be adressed using its node shortest prefix.
387 #
389 #
388 # The trie is stored as a sequence of block. Each block contains 16 entries
390 # The trie is stored as a sequence of block. Each block contains 16 entries
389 # (signed 64bit integer, big endian). Each entry can be one of the following:
391 # (signed 64bit integer, big endian). Each entry can be one of the following:
390 #
392 #
391 # * value >= 0 -> index of sub-block
393 # * value >= 0 -> index of sub-block
392 # * value == -1 -> no value
394 # * value == -1 -> no value
393 # * value < -1 -> a revision value: rev = -(value+10)
395 # * value < -1 -> a revision value: rev = -(value+10)
394 #
396 #
395 # The implementation focus on simplicity, not on performance. A Rust
397 # The implementation focus on simplicity, not on performance. A Rust
396 # implementation should provide a efficient version of the same binary
398 # implementation should provide a efficient version of the same binary
397 # persistence. This reference python implementation is never meant to be
399 # persistence. This reference python implementation is never meant to be
398 # extensively use in production.
400 # extensively use in production.
399
401
400
402
401 def persistent_data(index):
403 def persistent_data(index):
402 """return the persistent binary form for a nodemap for a given index
404 """return the persistent binary form for a nodemap for a given index
403 """
405 """
404 trie = _build_trie(index)
406 trie = _build_trie(index)
405 return _persist_trie(trie)
407 return _persist_trie(trie)
406
408
407
409
408 def update_persistent_data(index, root, max_idx, last_rev):
410 def update_persistent_data(index, root, max_idx, last_rev):
409 """return the incremental update for persistent nodemap from a given index
411 """return the incremental update for persistent nodemap from a given index
410 """
412 """
411 changed_block, trie = _update_trie(index, root, last_rev)
413 changed_block, trie = _update_trie(index, root, last_rev)
412 return (
414 return (
413 changed_block * S_BLOCK.size,
415 changed_block * S_BLOCK.size,
414 _persist_trie(trie, existing_idx=max_idx),
416 _persist_trie(trie, existing_idx=max_idx),
415 )
417 )
416
418
417
419
418 S_BLOCK = struct.Struct(">" + ("l" * 16))
420 S_BLOCK = struct.Struct(">" + ("l" * 16))
419
421
420 NO_ENTRY = -1
422 NO_ENTRY = -1
421 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
423 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
422 REV_OFFSET = 2
424 REV_OFFSET = 2
423
425
424
426
425 def _transform_rev(rev):
427 def _transform_rev(rev):
426 """Return the number used to represent the rev in the tree.
428 """Return the number used to represent the rev in the tree.
427
429
428 (or retrieve a rev number from such representation)
430 (or retrieve a rev number from such representation)
429
431
430 Note that this is an involution, a function equal to its inverse (i.e.
432 Note that this is an involution, a function equal to its inverse (i.e.
431 which gives the identity when applied to itself).
433 which gives the identity when applied to itself).
432 """
434 """
433 return -(rev + REV_OFFSET)
435 return -(rev + REV_OFFSET)
434
436
435
437
436 def _to_int(hex_digit):
438 def _to_int(hex_digit):
437 """turn an hexadecimal digit into a proper integer"""
439 """turn an hexadecimal digit into a proper integer"""
438 return int(hex_digit, 16)
440 return int(hex_digit, 16)
439
441
440
442
441 class Block(dict):
443 class Block(dict):
442 """represent a block of the Trie
444 """represent a block of the Trie
443
445
444 contains up to 16 entry indexed from 0 to 15"""
446 contains up to 16 entry indexed from 0 to 15"""
445
447
446 def __init__(self):
448 def __init__(self):
447 super(Block, self).__init__()
449 super(Block, self).__init__()
448 # If this block exist on disk, here is its ID
450 # If this block exist on disk, here is its ID
449 self.ondisk_id = None
451 self.ondisk_id = None
450
452
451 def __iter__(self):
453 def __iter__(self):
452 return iter(self.get(i) for i in range(16))
454 return iter(self.get(i) for i in range(16))
453
455
454
456
455 def _build_trie(index):
457 def _build_trie(index):
456 """build a nodemap trie
458 """build a nodemap trie
457
459
458 The nodemap stores revision number for each unique prefix.
460 The nodemap stores revision number for each unique prefix.
459
461
460 Each block is a dictionary with keys in `[0, 15]`. Values are either
462 Each block is a dictionary with keys in `[0, 15]`. Values are either
461 another block or a revision number.
463 another block or a revision number.
462 """
464 """
463 root = Block()
465 root = Block()
464 for rev in range(len(index)):
466 for rev in range(len(index)):
465 hex = nodemod.hex(index[rev][7])
467 hex = nodemod.hex(index[rev][7])
466 _insert_into_block(index, 0, root, rev, hex)
468 _insert_into_block(index, 0, root, rev, hex)
467 return root
469 return root
468
470
469
471
470 def _update_trie(index, root, last_rev):
472 def _update_trie(index, root, last_rev):
471 """consume"""
473 """consume"""
472 changed = 0
474 changed = 0
473 for rev in range(last_rev + 1, len(index)):
475 for rev in range(last_rev + 1, len(index)):
474 hex = nodemod.hex(index[rev][7])
476 hex = nodemod.hex(index[rev][7])
475 changed += _insert_into_block(index, 0, root, rev, hex)
477 changed += _insert_into_block(index, 0, root, rev, hex)
476 return changed, root
478 return changed, root
477
479
478
480
479 def _insert_into_block(index, level, block, current_rev, current_hex):
481 def _insert_into_block(index, level, block, current_rev, current_hex):
480 """insert a new revision in a block
482 """insert a new revision in a block
481
483
482 index: the index we are adding revision for
484 index: the index we are adding revision for
483 level: the depth of the current block in the trie
485 level: the depth of the current block in the trie
484 block: the block currently being considered
486 block: the block currently being considered
485 current_rev: the revision number we are adding
487 current_rev: the revision number we are adding
486 current_hex: the hexadecimal representation of the of that revision
488 current_hex: the hexadecimal representation of the of that revision
487 """
489 """
488 changed = 1
490 changed = 1
489 if block.ondisk_id is not None:
491 if block.ondisk_id is not None:
490 block.ondisk_id = None
492 block.ondisk_id = None
491 hex_digit = _to_int(current_hex[level : level + 1])
493 hex_digit = _to_int(current_hex[level : level + 1])
492 entry = block.get(hex_digit)
494 entry = block.get(hex_digit)
493 if entry is None:
495 if entry is None:
494 # no entry, simply store the revision number
496 # no entry, simply store the revision number
495 block[hex_digit] = current_rev
497 block[hex_digit] = current_rev
496 elif isinstance(entry, dict):
498 elif isinstance(entry, dict):
497 # need to recurse to an underlying block
499 # need to recurse to an underlying block
498 changed += _insert_into_block(
500 changed += _insert_into_block(
499 index, level + 1, entry, current_rev, current_hex
501 index, level + 1, entry, current_rev, current_hex
500 )
502 )
501 else:
503 else:
502 # collision with a previously unique prefix, inserting new
504 # collision with a previously unique prefix, inserting new
503 # vertices to fit both entry.
505 # vertices to fit both entry.
504 other_hex = nodemod.hex(index[entry][7])
506 other_hex = nodemod.hex(index[entry][7])
505 other_rev = entry
507 other_rev = entry
506 new = Block()
508 new = Block()
507 block[hex_digit] = new
509 block[hex_digit] = new
508 _insert_into_block(index, level + 1, new, other_rev, other_hex)
510 _insert_into_block(index, level + 1, new, other_rev, other_hex)
509 _insert_into_block(index, level + 1, new, current_rev, current_hex)
511 _insert_into_block(index, level + 1, new, current_rev, current_hex)
510 return changed
512 return changed
511
513
512
514
513 def _persist_trie(root, existing_idx=None):
515 def _persist_trie(root, existing_idx=None):
514 """turn a nodemap trie into persistent binary data
516 """turn a nodemap trie into persistent binary data
515
517
516 See `_build_trie` for nodemap trie structure"""
518 See `_build_trie` for nodemap trie structure"""
517 block_map = {}
519 block_map = {}
518 if existing_idx is not None:
520 if existing_idx is not None:
519 base_idx = existing_idx + 1
521 base_idx = existing_idx + 1
520 else:
522 else:
521 base_idx = 0
523 base_idx = 0
522 chunks = []
524 chunks = []
523 for tn in _walk_trie(root):
525 for tn in _walk_trie(root):
524 if tn.ondisk_id is not None:
526 if tn.ondisk_id is not None:
525 block_map[id(tn)] = tn.ondisk_id
527 block_map[id(tn)] = tn.ondisk_id
526 else:
528 else:
527 block_map[id(tn)] = len(chunks) + base_idx
529 block_map[id(tn)] = len(chunks) + base_idx
528 chunks.append(_persist_block(tn, block_map))
530 chunks.append(_persist_block(tn, block_map))
529 return b''.join(chunks)
531 return b''.join(chunks)
530
532
531
533
532 def _walk_trie(block):
534 def _walk_trie(block):
533 """yield all the block in a trie
535 """yield all the block in a trie
534
536
535 Children blocks are always yield before their parent block.
537 Children blocks are always yield before their parent block.
536 """
538 """
537 for (__, item) in sorted(block.items()):
539 for (__, item) in sorted(block.items()):
538 if isinstance(item, dict):
540 if isinstance(item, dict):
539 for sub_block in _walk_trie(item):
541 for sub_block in _walk_trie(item):
540 yield sub_block
542 yield sub_block
541 yield block
543 yield block
542
544
543
545
544 def _persist_block(block_node, block_map):
546 def _persist_block(block_node, block_map):
545 """produce persistent binary data for a single block
547 """produce persistent binary data for a single block
546
548
547 Children block are assumed to be already persisted and present in
549 Children block are assumed to be already persisted and present in
548 block_map.
550 block_map.
549 """
551 """
550 data = tuple(_to_value(v, block_map) for v in block_node)
552 data = tuple(_to_value(v, block_map) for v in block_node)
551 return S_BLOCK.pack(*data)
553 return S_BLOCK.pack(*data)
552
554
553
555
554 def _to_value(item, block_map):
556 def _to_value(item, block_map):
555 """persist any value as an integer"""
557 """persist any value as an integer"""
556 if item is None:
558 if item is None:
557 return NO_ENTRY
559 return NO_ENTRY
558 elif isinstance(item, dict):
560 elif isinstance(item, dict):
559 return block_map[id(item)]
561 return block_map[id(item)]
560 else:
562 else:
561 return _transform_rev(item)
563 return _transform_rev(item)
562
564
563
565
564 def parse_data(data):
566 def parse_data(data):
565 """parse parse nodemap data into a nodemap Trie"""
567 """parse parse nodemap data into a nodemap Trie"""
566 if (len(data) % S_BLOCK.size) != 0:
568 if (len(data) % S_BLOCK.size) != 0:
567 msg = "nodemap data size is not a multiple of block size (%d): %d"
569 msg = "nodemap data size is not a multiple of block size (%d): %d"
568 raise error.Abort(msg % (S_BLOCK.size, len(data)))
570 raise error.Abort(msg % (S_BLOCK.size, len(data)))
569 if not data:
571 if not data:
570 return Block(), None
572 return Block(), None
571 block_map = {}
573 block_map = {}
572 new_blocks = []
574 new_blocks = []
573 for i in range(0, len(data), S_BLOCK.size):
575 for i in range(0, len(data), S_BLOCK.size):
574 block = Block()
576 block = Block()
575 block.ondisk_id = len(block_map)
577 block.ondisk_id = len(block_map)
576 block_map[block.ondisk_id] = block
578 block_map[block.ondisk_id] = block
577 block_data = data[i : i + S_BLOCK.size]
579 block_data = data[i : i + S_BLOCK.size]
578 values = S_BLOCK.unpack(block_data)
580 values = S_BLOCK.unpack(block_data)
579 new_blocks.append((block, values))
581 new_blocks.append((block, values))
580 for b, values in new_blocks:
582 for b, values in new_blocks:
581 for idx, v in enumerate(values):
583 for idx, v in enumerate(values):
582 if v == NO_ENTRY:
584 if v == NO_ENTRY:
583 continue
585 continue
584 elif v >= 0:
586 elif v >= 0:
585 b[idx] = block_map[v]
587 b[idx] = block_map[v]
586 else:
588 else:
587 b[idx] = _transform_rev(v)
589 b[idx] = _transform_rev(v)
588 return block, i // S_BLOCK.size
590 return block, i // S_BLOCK.size
589
591
590
592
591 # debug utility
593 # debug utility
592
594
593
595
594 def check_data(ui, index, data):
596 def check_data(ui, index, data):
595 """verify that the provided nodemap data are valid for the given idex"""
597 """verify that the provided nodemap data are valid for the given idex"""
596 ret = 0
598 ret = 0
597 ui.status((b"revision in index: %d\n") % len(index))
599 ui.status((b"revision in index: %d\n") % len(index))
598 root, __ = parse_data(data)
600 root, __ = parse_data(data)
599 all_revs = set(_all_revisions(root))
601 all_revs = set(_all_revisions(root))
600 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
602 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
601 for r in range(len(index)):
603 for r in range(len(index)):
602 if r not in all_revs:
604 if r not in all_revs:
603 msg = b" revision missing from nodemap: %d\n" % r
605 msg = b" revision missing from nodemap: %d\n" % r
604 ui.write_err(msg)
606 ui.write_err(msg)
605 ret = 1
607 ret = 1
606 else:
608 else:
607 all_revs.remove(r)
609 all_revs.remove(r)
608 nm_rev = _find_node(root, nodemod.hex(index[r][7]))
610 nm_rev = _find_node(root, nodemod.hex(index[r][7]))
609 if nm_rev is None:
611 if nm_rev is None:
610 msg = b" revision node does not match any entries: %d\n" % r
612 msg = b" revision node does not match any entries: %d\n" % r
611 ui.write_err(msg)
613 ui.write_err(msg)
612 ret = 1
614 ret = 1
613 elif nm_rev != r:
615 elif nm_rev != r:
614 msg = (
616 msg = (
615 b" revision node does not match the expected revision: "
617 b" revision node does not match the expected revision: "
616 b"%d != %d\n" % (r, nm_rev)
618 b"%d != %d\n" % (r, nm_rev)
617 )
619 )
618 ui.write_err(msg)
620 ui.write_err(msg)
619 ret = 1
621 ret = 1
620
622
621 if all_revs:
623 if all_revs:
622 for r in sorted(all_revs):
624 for r in sorted(all_revs):
623 msg = b" extra revision in nodemap: %d\n" % r
625 msg = b" extra revision in nodemap: %d\n" % r
624 ui.write_err(msg)
626 ui.write_err(msg)
625 ret = 1
627 ret = 1
626 return ret
628 return ret
627
629
628
630
629 def _all_revisions(root):
631 def _all_revisions(root):
630 """return all revisions stored in a Trie"""
632 """return all revisions stored in a Trie"""
631 for block in _walk_trie(root):
633 for block in _walk_trie(root):
632 for v in block:
634 for v in block:
633 if v is None or isinstance(v, Block):
635 if v is None or isinstance(v, Block):
634 continue
636 continue
635 yield v
637 yield v
636
638
637
639
638 def _find_node(block, node):
640 def _find_node(block, node):
639 """find the revision associated with a given node"""
641 """find the revision associated with a given node"""
640 entry = block.get(_to_int(node[0:1]))
642 entry = block.get(_to_int(node[0:1]))
641 if isinstance(entry, dict):
643 if isinstance(entry, dict):
642 return _find_node(entry, node[1:])
644 return _find_node(entry, node[1:])
643 return entry
645 return entry
@@ -1,418 +1,429 b''
1 ===================================
1 ===================================
2 Test the persistent on-disk nodemap
2 Test the persistent on-disk nodemap
3 ===================================
3 ===================================
4
4
5 $ hg init test-repo
5 $ hg init test-repo
6 $ cd test-repo
6 $ cd test-repo
7 $ cat << EOF >> .hg/hgrc
7 $ cat << EOF >> .hg/hgrc
8 > [experimental]
8 > [experimental]
9 > exp-persistent-nodemap=yes
9 > exp-persistent-nodemap=yes
10 > [devel]
10 > [devel]
11 > persistent-nodemap=yes
11 > persistent-nodemap=yes
12 > EOF
12 > EOF
13 $ hg debugbuilddag .+5000 --new-file --config "experimental.exp-persistent-nodemap.mode=warn"
13 $ hg debugbuilddag .+5000 --new-file --config "experimental.exp-persistent-nodemap.mode=warn"
14 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
14 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
15 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
15 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
16 $ hg debugnodemap --metadata
16 $ hg debugnodemap --metadata
17 uid: ???????????????? (glob)
17 uid: ???????????????? (glob)
18 tip-rev: 5000
18 tip-rev: 5000
19 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
19 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
20 data-length: 121088
20 data-length: 121088
21 data-unused: 0
21 data-unused: 0
22 data-unused: 0.000%
22 data-unused: 0.000%
23 $ f --size .hg/store/00changelog.n
23 $ f --size .hg/store/00changelog.n
24 .hg/store/00changelog.n: size=70
24 .hg/store/00changelog.n: size=70
25
25
26 Simple lookup works
26 Simple lookup works
27
27
28 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
28 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
29 $ hg log -r "$ANYNODE" --template '{rev}\n'
29 $ hg log -r "$ANYNODE" --template '{rev}\n'
30 5000
30 5000
31
31
32
32
33 #if rust
33 #if rust
34
34
35 $ f --sha256 .hg/store/00changelog-*.nd
35 $ f --sha256 .hg/store/00changelog-*.nd
36 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
36 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
37
37
38 $ f --sha256 .hg/store/00manifest-*.nd
38 $ f --sha256 .hg/store/00manifest-*.nd
39 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
39 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
40 $ hg debugnodemap --dump-new | f --sha256 --size
40 $ hg debugnodemap --dump-new | f --sha256 --size
41 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
41 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
42 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
42 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
43 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
43 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
44 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
44 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
45 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
45 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
46 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
46 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
47 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
47 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
48 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
48 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
49 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
49 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
50 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
50 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
51 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
51 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
52 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
52 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
53 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
53 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
54 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
54 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
55 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
55 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
56 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
56 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
57 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
57 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
58 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
58 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
59 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
59 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
60
60
61
61
62 #else
62 #else
63
63
64 $ f --sha256 .hg/store/00changelog-*.nd
64 $ f --sha256 .hg/store/00changelog-*.nd
65 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
65 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
66 $ hg debugnodemap --dump-new | f --sha256 --size
66 $ hg debugnodemap --dump-new | f --sha256 --size
67 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
67 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
68 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
68 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
69 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
69 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
70 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
70 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
71 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
71 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
72 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
72 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
73 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
73 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
74 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
74 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
75 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
75 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
76 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
76 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
77 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
77 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
78 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
78 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
79 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
79 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
80 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
80 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
81 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
81 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
82 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
82 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
83 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
83 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
84 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
84 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
85 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
85 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
86
86
87 #endif
87 #endif
88
88
89 $ hg debugnodemap --check
89 $ hg debugnodemap --check
90 revision in index: 5001
90 revision in index: 5001
91 revision in nodemap: 5001
91 revision in nodemap: 5001
92
92
93 add a new commit
93 add a new commit
94
94
95 $ hg up
95 $ hg up
96 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 $ echo foo > foo
97 $ echo foo > foo
98 $ hg add foo
98 $ hg add foo
99
100 #if no-pure no-rust
101
102 $ hg ci -m 'foo' --config "experimental.exp-persistent-nodemap.mode=strict"
103 transaction abort!
104 rollback completed
105 abort: persistent nodemap in strict mode without efficient method
106 [255]
107
108 #endif
109
99 $ hg ci -m 'foo'
110 $ hg ci -m 'foo'
100
111
101 #if no-pure no-rust
112 #if no-pure no-rust
102 $ hg debugnodemap --metadata
113 $ hg debugnodemap --metadata
103 uid: ???????????????? (glob)
114 uid: ???????????????? (glob)
104 tip-rev: 5001
115 tip-rev: 5001
105 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
116 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
106 data-length: 121088
117 data-length: 121088
107 data-unused: 0
118 data-unused: 0
108 data-unused: 0.000%
119 data-unused: 0.000%
109 #else
120 #else
110 $ hg debugnodemap --metadata
121 $ hg debugnodemap --metadata
111 uid: ???????????????? (glob)
122 uid: ???????????????? (glob)
112 tip-rev: 5001
123 tip-rev: 5001
113 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
124 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
114 data-length: 121344
125 data-length: 121344
115 data-unused: 256
126 data-unused: 256
116 data-unused: 0.211%
127 data-unused: 0.211%
117 #endif
128 #endif
118
129
119 $ f --size .hg/store/00changelog.n
130 $ f --size .hg/store/00changelog.n
120 .hg/store/00changelog.n: size=70
131 .hg/store/00changelog.n: size=70
121
132
122 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
133 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
123
134
124 #if pure
135 #if pure
125 $ f --sha256 .hg/store/00changelog-*.nd --size
136 $ f --sha256 .hg/store/00changelog-*.nd --size
126 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
137 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
127 #endif
138 #endif
128
139
129 #if rust
140 #if rust
130 $ f --sha256 .hg/store/00changelog-*.nd --size
141 $ f --sha256 .hg/store/00changelog-*.nd --size
131 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
142 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
132 #endif
143 #endif
133
144
134 #if no-pure no-rust
145 #if no-pure no-rust
135 $ f --sha256 .hg/store/00changelog-*.nd --size
146 $ f --sha256 .hg/store/00changelog-*.nd --size
136 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
147 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
137 #endif
148 #endif
138
149
139 $ hg debugnodemap --check
150 $ hg debugnodemap --check
140 revision in index: 5002
151 revision in index: 5002
141 revision in nodemap: 5002
152 revision in nodemap: 5002
142
153
143 Test code path without mmap
154 Test code path without mmap
144 ---------------------------
155 ---------------------------
145
156
146 $ echo bar > bar
157 $ echo bar > bar
147 $ hg add bar
158 $ hg add bar
148 $ hg ci -m 'bar' --config experimental.exp-persistent-nodemap.mmap=no
159 $ hg ci -m 'bar' --config experimental.exp-persistent-nodemap.mmap=no
149
160
150 $ hg debugnodemap --check --config experimental.exp-persistent-nodemap.mmap=yes
161 $ hg debugnodemap --check --config experimental.exp-persistent-nodemap.mmap=yes
151 revision in index: 5003
162 revision in index: 5003
152 revision in nodemap: 5003
163 revision in nodemap: 5003
153 $ hg debugnodemap --check --config experimental.exp-persistent-nodemap.mmap=no
164 $ hg debugnodemap --check --config experimental.exp-persistent-nodemap.mmap=no
154 revision in index: 5003
165 revision in index: 5003
155 revision in nodemap: 5003
166 revision in nodemap: 5003
156
167
157
168
158 #if pure
169 #if pure
159 $ hg debugnodemap --metadata
170 $ hg debugnodemap --metadata
160 uid: ???????????????? (glob)
171 uid: ???????????????? (glob)
161 tip-rev: 5002
172 tip-rev: 5002
162 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
173 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
163 data-length: 121600
174 data-length: 121600
164 data-unused: 512
175 data-unused: 512
165 data-unused: 0.421%
176 data-unused: 0.421%
166 $ f --sha256 .hg/store/00changelog-*.nd --size
177 $ f --sha256 .hg/store/00changelog-*.nd --size
167 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
178 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
168 #endif
179 #endif
169 #if rust
180 #if rust
170 $ hg debugnodemap --metadata
181 $ hg debugnodemap --metadata
171 uid: ???????????????? (glob)
182 uid: ???????????????? (glob)
172 tip-rev: 5002
183 tip-rev: 5002
173 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
184 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
174 data-length: 121600
185 data-length: 121600
175 data-unused: 512
186 data-unused: 512
176 data-unused: 0.421%
187 data-unused: 0.421%
177 $ f --sha256 .hg/store/00changelog-*.nd --size
188 $ f --sha256 .hg/store/00changelog-*.nd --size
178 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
189 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
179 #endif
190 #endif
180 #if no-pure no-rust
191 #if no-pure no-rust
181 $ hg debugnodemap --metadata
192 $ hg debugnodemap --metadata
182 uid: ???????????????? (glob)
193 uid: ???????????????? (glob)
183 tip-rev: 5002
194 tip-rev: 5002
184 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
195 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
185 data-length: 121088
196 data-length: 121088
186 data-unused: 0
197 data-unused: 0
187 data-unused: 0.000%
198 data-unused: 0.000%
188 $ f --sha256 .hg/store/00changelog-*.nd --size
199 $ f --sha256 .hg/store/00changelog-*.nd --size
189 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
200 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
190 #endif
201 #endif
191
202
192 Test force warming the cache
203 Test force warming the cache
193
204
194 $ rm .hg/store/00changelog.n
205 $ rm .hg/store/00changelog.n
195 $ hg debugnodemap --metadata
206 $ hg debugnodemap --metadata
196 $ hg debugupdatecache
207 $ hg debugupdatecache
197 #if pure
208 #if pure
198 $ hg debugnodemap --metadata
209 $ hg debugnodemap --metadata
199 uid: ???????????????? (glob)
210 uid: ???????????????? (glob)
200 tip-rev: 5002
211 tip-rev: 5002
201 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
212 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
202 data-length: 121088
213 data-length: 121088
203 data-unused: 0
214 data-unused: 0
204 data-unused: 0.000%
215 data-unused: 0.000%
205 #else
216 #else
206 $ hg debugnodemap --metadata
217 $ hg debugnodemap --metadata
207 uid: ???????????????? (glob)
218 uid: ???????????????? (glob)
208 tip-rev: 5002
219 tip-rev: 5002
209 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
220 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
210 data-length: 121088
221 data-length: 121088
211 data-unused: 0
222 data-unused: 0
212 data-unused: 0.000%
223 data-unused: 0.000%
213 #endif
224 #endif
214
225
215 Check out of sync nodemap
226 Check out of sync nodemap
216 =========================
227 =========================
217
228
218 First copy old data on the side.
229 First copy old data on the side.
219
230
220 $ mkdir ../tmp-copies
231 $ mkdir ../tmp-copies
221 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
232 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
222
233
223 Nodemap lagging behind
234 Nodemap lagging behind
224 ----------------------
235 ----------------------
225
236
226 make a new commit
237 make a new commit
227
238
228 $ echo bar2 > bar
239 $ echo bar2 > bar
229 $ hg ci -m 'bar2'
240 $ hg ci -m 'bar2'
230 $ NODE=`hg log -r tip -T '{node}\n'`
241 $ NODE=`hg log -r tip -T '{node}\n'`
231 $ hg log -r "$NODE" -T '{rev}\n'
242 $ hg log -r "$NODE" -T '{rev}\n'
232 5003
243 5003
233
244
234 If the nodemap is lagging behind, it can catch up fine
245 If the nodemap is lagging behind, it can catch up fine
235
246
236 $ hg debugnodemap --metadata
247 $ hg debugnodemap --metadata
237 uid: ???????????????? (glob)
248 uid: ???????????????? (glob)
238 tip-rev: 5003
249 tip-rev: 5003
239 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
250 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
240 data-length: 121344 (pure !)
251 data-length: 121344 (pure !)
241 data-length: 121344 (rust !)
252 data-length: 121344 (rust !)
242 data-length: 121152 (no-rust no-pure !)
253 data-length: 121152 (no-rust no-pure !)
243 data-unused: 192 (pure !)
254 data-unused: 192 (pure !)
244 data-unused: 192 (rust !)
255 data-unused: 192 (rust !)
245 data-unused: 0 (no-rust no-pure !)
256 data-unused: 0 (no-rust no-pure !)
246 data-unused: 0.158% (pure !)
257 data-unused: 0.158% (pure !)
247 data-unused: 0.158% (rust !)
258 data-unused: 0.158% (rust !)
248 data-unused: 0.000% (no-rust no-pure !)
259 data-unused: 0.000% (no-rust no-pure !)
249 $ cp -f ../tmp-copies/* .hg/store/
260 $ cp -f ../tmp-copies/* .hg/store/
250 $ hg debugnodemap --metadata
261 $ hg debugnodemap --metadata
251 uid: ???????????????? (glob)
262 uid: ???????????????? (glob)
252 tip-rev: 5002
263 tip-rev: 5002
253 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
264 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
254 data-length: 121088
265 data-length: 121088
255 data-unused: 0
266 data-unused: 0
256 data-unused: 0.000%
267 data-unused: 0.000%
257 $ hg log -r "$NODE" -T '{rev}\n'
268 $ hg log -r "$NODE" -T '{rev}\n'
258 5003
269 5003
259
270
260 changelog altered
271 changelog altered
261 -----------------
272 -----------------
262
273
263 If the nodemap is not gated behind a requirements, an unaware client can alter
274 If the nodemap is not gated behind a requirements, an unaware client can alter
264 the repository so the revlog used to generate the nodemap is not longer
275 the repository so the revlog used to generate the nodemap is not longer
265 compatible with the persistent nodemap. We need to detect that.
276 compatible with the persistent nodemap. We need to detect that.
266
277
267 $ hg up "$NODE~5"
278 $ hg up "$NODE~5"
268 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
279 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
269 $ echo bar > babar
280 $ echo bar > babar
270 $ hg add babar
281 $ hg add babar
271 $ hg ci -m 'babar'
282 $ hg ci -m 'babar'
272 created new head
283 created new head
273 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
284 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
274 $ hg log -r "$OTHERNODE" -T '{rev}\n'
285 $ hg log -r "$OTHERNODE" -T '{rev}\n'
275 5004
286 5004
276
287
277 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
288 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
278
289
279 the nodemap should detect the changelog have been tampered with and recover.
290 the nodemap should detect the changelog have been tampered with and recover.
280
291
281 $ hg debugnodemap --metadata
292 $ hg debugnodemap --metadata
282 uid: ???????????????? (glob)
293 uid: ???????????????? (glob)
283 tip-rev: 5002
294 tip-rev: 5002
284 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
295 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
285 data-length: 121536 (pure !)
296 data-length: 121536 (pure !)
286 data-length: 121088 (rust !)
297 data-length: 121088 (rust !)
287 data-length: 121088 (no-pure no-rust !)
298 data-length: 121088 (no-pure no-rust !)
288 data-unused: 448 (pure !)
299 data-unused: 448 (pure !)
289 data-unused: 0 (rust !)
300 data-unused: 0 (rust !)
290 data-unused: 0 (no-pure no-rust !)
301 data-unused: 0 (no-pure no-rust !)
291 data-unused: 0.000% (rust !)
302 data-unused: 0.000% (rust !)
292 data-unused: 0.369% (pure !)
303 data-unused: 0.369% (pure !)
293 data-unused: 0.000% (no-pure no-rust !)
304 data-unused: 0.000% (no-pure no-rust !)
294
305
295 $ cp -f ../tmp-copies/* .hg/store/
306 $ cp -f ../tmp-copies/* .hg/store/
296 $ hg debugnodemap --metadata
307 $ hg debugnodemap --metadata
297 uid: ???????????????? (glob)
308 uid: ???????????????? (glob)
298 tip-rev: 5002
309 tip-rev: 5002
299 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
310 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
300 data-length: 121088
311 data-length: 121088
301 data-unused: 0
312 data-unused: 0
302 data-unused: 0.000%
313 data-unused: 0.000%
303 $ hg log -r "$OTHERNODE" -T '{rev}\n'
314 $ hg log -r "$OTHERNODE" -T '{rev}\n'
304 5002
315 5002
305
316
306 Check transaction related property
317 Check transaction related property
307 ==================================
318 ==================================
308
319
309 An up to date nodemap should be available to shell hooks,
320 An up to date nodemap should be available to shell hooks,
310
321
311 $ echo dsljfl > a
322 $ echo dsljfl > a
312 $ hg add a
323 $ hg add a
313 $ hg ci -m a
324 $ hg ci -m a
314 $ hg debugnodemap --metadata
325 $ hg debugnodemap --metadata
315 uid: ???????????????? (glob)
326 uid: ???????????????? (glob)
316 tip-rev: 5003
327 tip-rev: 5003
317 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
328 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
318 data-length: 121088
329 data-length: 121088
319 data-unused: 0
330 data-unused: 0
320 data-unused: 0.000%
331 data-unused: 0.000%
321 $ echo babar2 > babar
332 $ echo babar2 > babar
322 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
333 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
323 uid: ???????????????? (glob)
334 uid: ???????????????? (glob)
324 tip-rev: 5004
335 tip-rev: 5004
325 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
336 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
326 data-length: 121280 (pure !)
337 data-length: 121280 (pure !)
327 data-length: 121280 (rust !)
338 data-length: 121280 (rust !)
328 data-length: 121088 (no-pure no-rust !)
339 data-length: 121088 (no-pure no-rust !)
329 data-unused: 192 (pure !)
340 data-unused: 192 (pure !)
330 data-unused: 192 (rust !)
341 data-unused: 192 (rust !)
331 data-unused: 0 (no-pure no-rust !)
342 data-unused: 0 (no-pure no-rust !)
332 data-unused: 0.158% (pure !)
343 data-unused: 0.158% (pure !)
333 data-unused: 0.158% (rust !)
344 data-unused: 0.158% (rust !)
334 data-unused: 0.000% (no-pure no-rust !)
345 data-unused: 0.000% (no-pure no-rust !)
335 $ hg debugnodemap --metadata
346 $ hg debugnodemap --metadata
336 uid: ???????????????? (glob)
347 uid: ???????????????? (glob)
337 tip-rev: 5004
348 tip-rev: 5004
338 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
349 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
339 data-length: 121280 (pure !)
350 data-length: 121280 (pure !)
340 data-length: 121280 (rust !)
351 data-length: 121280 (rust !)
341 data-length: 121088 (no-pure no-rust !)
352 data-length: 121088 (no-pure no-rust !)
342 data-unused: 192 (pure !)
353 data-unused: 192 (pure !)
343 data-unused: 192 (rust !)
354 data-unused: 192 (rust !)
344 data-unused: 0 (no-pure no-rust !)
355 data-unused: 0 (no-pure no-rust !)
345 data-unused: 0.158% (pure !)
356 data-unused: 0.158% (pure !)
346 data-unused: 0.158% (rust !)
357 data-unused: 0.158% (rust !)
347 data-unused: 0.000% (no-pure no-rust !)
358 data-unused: 0.000% (no-pure no-rust !)
348
359
349 Another process does not see the pending nodemap content during run.
360 Another process does not see the pending nodemap content during run.
350
361
351 $ PATH=$RUNTESTDIR/testlib/:$PATH
362 $ PATH=$RUNTESTDIR/testlib/:$PATH
352 $ echo qpoasp > a
363 $ echo qpoasp > a
353 $ hg ci -m a2 \
364 $ hg ci -m a2 \
354 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
365 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
355 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
366 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
356
367
357 (read the repository while the commit transaction is pending)
368 (read the repository while the commit transaction is pending)
358
369
359 $ wait-on-file 20 sync-txn-pending && \
370 $ wait-on-file 20 sync-txn-pending && \
360 > hg debugnodemap --metadata && \
371 > hg debugnodemap --metadata && \
361 > wait-on-file 20 sync-txn-close sync-repo-read
372 > wait-on-file 20 sync-txn-close sync-repo-read
362 uid: ???????????????? (glob)
373 uid: ???????????????? (glob)
363 tip-rev: 5004
374 tip-rev: 5004
364 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
375 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
365 data-length: 121280 (pure !)
376 data-length: 121280 (pure !)
366 data-length: 121280 (rust !)
377 data-length: 121280 (rust !)
367 data-length: 121088 (no-pure no-rust !)
378 data-length: 121088 (no-pure no-rust !)
368 data-unused: 192 (pure !)
379 data-unused: 192 (pure !)
369 data-unused: 192 (rust !)
380 data-unused: 192 (rust !)
370 data-unused: 0 (no-pure no-rust !)
381 data-unused: 0 (no-pure no-rust !)
371 data-unused: 0.158% (pure !)
382 data-unused: 0.158% (pure !)
372 data-unused: 0.158% (rust !)
383 data-unused: 0.158% (rust !)
373 data-unused: 0.000% (no-pure no-rust !)
384 data-unused: 0.000% (no-pure no-rust !)
374 $ hg debugnodemap --metadata
385 $ hg debugnodemap --metadata
375 uid: ???????????????? (glob)
386 uid: ???????????????? (glob)
376 tip-rev: 5005
387 tip-rev: 5005
377 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
388 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
378 data-length: 121536 (pure !)
389 data-length: 121536 (pure !)
379 data-length: 121536 (rust !)
390 data-length: 121536 (rust !)
380 data-length: 121088 (no-pure no-rust !)
391 data-length: 121088 (no-pure no-rust !)
381 data-unused: 448 (pure !)
392 data-unused: 448 (pure !)
382 data-unused: 448 (rust !)
393 data-unused: 448 (rust !)
383 data-unused: 0 (no-pure no-rust !)
394 data-unused: 0 (no-pure no-rust !)
384 data-unused: 0.369% (pure !)
395 data-unused: 0.369% (pure !)
385 data-unused: 0.369% (rust !)
396 data-unused: 0.369% (rust !)
386 data-unused: 0.000% (no-pure no-rust !)
397 data-unused: 0.000% (no-pure no-rust !)
387
398
388 $ cat output.txt
399 $ cat output.txt
389
400
390 Check that a failing transaction will properly revert the data
401 Check that a failing transaction will properly revert the data
391
402
392 $ echo plakfe > a
403 $ echo plakfe > a
393 $ f --size --sha256 .hg/store/00changelog-*.nd
404 $ f --size --sha256 .hg/store/00changelog-*.nd
394 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
405 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
395 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
406 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
396 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
407 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
397 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
408 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
398 transaction abort!
409 transaction abort!
399 rollback completed
410 rollback completed
400 abort: This is a late abort
411 abort: This is a late abort
401 [255]
412 [255]
402 $ hg debugnodemap --metadata
413 $ hg debugnodemap --metadata
403 uid: ???????????????? (glob)
414 uid: ???????????????? (glob)
404 tip-rev: 5005
415 tip-rev: 5005
405 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
416 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
406 data-length: 121536 (pure !)
417 data-length: 121536 (pure !)
407 data-length: 121536 (rust !)
418 data-length: 121536 (rust !)
408 data-length: 121088 (no-pure no-rust !)
419 data-length: 121088 (no-pure no-rust !)
409 data-unused: 448 (pure !)
420 data-unused: 448 (pure !)
410 data-unused: 448 (rust !)
421 data-unused: 448 (rust !)
411 data-unused: 0 (no-pure no-rust !)
422 data-unused: 0 (no-pure no-rust !)
412 data-unused: 0.369% (pure !)
423 data-unused: 0.369% (pure !)
413 data-unused: 0.369% (rust !)
424 data-unused: 0.369% (rust !)
414 data-unused: 0.000% (no-pure no-rust !)
425 data-unused: 0.000% (no-pure no-rust !)
415 $ f --size --sha256 .hg/store/00changelog-*.nd
426 $ f --size --sha256 .hg/store/00changelog-*.nd
416 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
427 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
417 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
428 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
418 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
429 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
General Comments 0
You need to be logged in to leave comments. Login now