##// END OF EJS Templates
flagutil: move insertflagprocessor to the new module (API)
marmoute -
r42957:5109217a default
parent child Browse files
Show More
@@ -1,2698 +1,2686 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@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 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 import collections
16 import collections
17 import contextlib
17 import contextlib
18 import errno
18 import errno
19 import io
19 import io
20 import os
20 import os
21 import struct
21 import struct
22 import zlib
22 import zlib
23
23
24 # import stuff from node for others to import from revlog
24 # import stuff from node for others to import from revlog
25 from .node import (
25 from .node import (
26 bin,
26 bin,
27 hex,
27 hex,
28 nullhex,
28 nullhex,
29 nullid,
29 nullid,
30 nullrev,
30 nullrev,
31 short,
31 short,
32 wdirfilenodeids,
32 wdirfilenodeids,
33 wdirhex,
33 wdirhex,
34 wdirid,
34 wdirid,
35 wdirrev,
35 wdirrev,
36 )
36 )
37 from .i18n import _
37 from .i18n import _
38 from .revlogutils.constants import (
38 from .revlogutils.constants import (
39 FLAG_GENERALDELTA,
39 FLAG_GENERALDELTA,
40 FLAG_INLINE_DATA,
40 FLAG_INLINE_DATA,
41 REVLOGV0,
41 REVLOGV0,
42 REVLOGV1,
42 REVLOGV1,
43 REVLOGV1_FLAGS,
43 REVLOGV1_FLAGS,
44 REVLOGV2,
44 REVLOGV2,
45 REVLOGV2_FLAGS,
45 REVLOGV2_FLAGS,
46 REVLOG_DEFAULT_FLAGS,
46 REVLOG_DEFAULT_FLAGS,
47 REVLOG_DEFAULT_FORMAT,
47 REVLOG_DEFAULT_FORMAT,
48 REVLOG_DEFAULT_VERSION,
48 REVLOG_DEFAULT_VERSION,
49 )
49 )
50 from .revlogutils.flagutil import (
50 from .revlogutils.flagutil import (
51 REVIDX_DEFAULT_FLAGS,
51 REVIDX_DEFAULT_FLAGS,
52 REVIDX_ELLIPSIS,
52 REVIDX_ELLIPSIS,
53 REVIDX_EXTSTORED,
53 REVIDX_EXTSTORED,
54 REVIDX_FLAGS_ORDER,
54 REVIDX_FLAGS_ORDER,
55 REVIDX_ISCENSORED,
55 REVIDX_ISCENSORED,
56 REVIDX_RAWTEXT_CHANGING_FLAGS,
56 REVIDX_RAWTEXT_CHANGING_FLAGS,
57 )
57 )
58 from .thirdparty import (
58 from .thirdparty import (
59 attr,
59 attr,
60 )
60 )
61 from . import (
61 from . import (
62 ancestor,
62 ancestor,
63 dagop,
63 dagop,
64 error,
64 error,
65 mdiff,
65 mdiff,
66 policy,
66 policy,
67 pycompat,
67 pycompat,
68 repository,
68 repository,
69 templatefilters,
69 templatefilters,
70 util,
70 util,
71 )
71 )
72 from .revlogutils import (
72 from .revlogutils import (
73 deltas as deltautil,
73 deltas as deltautil,
74 flagutil,
74 flagutil,
75 )
75 )
76 from .utils import (
76 from .utils import (
77 interfaceutil,
77 interfaceutil,
78 storageutil,
78 storageutil,
79 stringutil,
79 stringutil,
80 )
80 )
81
81
82 # blanked usage of all the name to prevent pyflakes constraints
82 # blanked usage of all the name to prevent pyflakes constraints
83 # We need these name available in the module for extensions.
83 # We need these name available in the module for extensions.
84 REVLOGV0
84 REVLOGV0
85 REVLOGV1
85 REVLOGV1
86 REVLOGV2
86 REVLOGV2
87 FLAG_INLINE_DATA
87 FLAG_INLINE_DATA
88 FLAG_GENERALDELTA
88 FLAG_GENERALDELTA
89 REVLOG_DEFAULT_FLAGS
89 REVLOG_DEFAULT_FLAGS
90 REVLOG_DEFAULT_FORMAT
90 REVLOG_DEFAULT_FORMAT
91 REVLOG_DEFAULT_VERSION
91 REVLOG_DEFAULT_VERSION
92 REVLOGV1_FLAGS
92 REVLOGV1_FLAGS
93 REVLOGV2_FLAGS
93 REVLOGV2_FLAGS
94 REVIDX_ISCENSORED
94 REVIDX_ISCENSORED
95 REVIDX_ELLIPSIS
95 REVIDX_ELLIPSIS
96 REVIDX_EXTSTORED
96 REVIDX_EXTSTORED
97 REVIDX_DEFAULT_FLAGS
97 REVIDX_DEFAULT_FLAGS
98 REVIDX_FLAGS_ORDER
98 REVIDX_FLAGS_ORDER
99 REVIDX_RAWTEXT_CHANGING_FLAGS
99 REVIDX_RAWTEXT_CHANGING_FLAGS
100
100
101 parsers = policy.importmod(r'parsers')
101 parsers = policy.importmod(r'parsers')
102 rustancestor = policy.importrust(r'ancestor')
102 rustancestor = policy.importrust(r'ancestor')
103 rustdagop = policy.importrust(r'dagop')
103 rustdagop = policy.importrust(r'dagop')
104
104
105 # Aliased for performance.
105 # Aliased for performance.
106 _zlibdecompress = zlib.decompress
106 _zlibdecompress = zlib.decompress
107
107
108 # max size of revlog with inline data
108 # max size of revlog with inline data
109 _maxinline = 131072
109 _maxinline = 131072
110 _chunksize = 1048576
110 _chunksize = 1048576
111
111
112 # Flag processors for REVIDX_ELLIPSIS.
112 # Flag processors for REVIDX_ELLIPSIS.
113 def ellipsisreadprocessor(rl, text):
113 def ellipsisreadprocessor(rl, text):
114 return text, False
114 return text, False
115
115
116 def ellipsiswriteprocessor(rl, text):
116 def ellipsiswriteprocessor(rl, text):
117 return text, False
117 return text, False
118
118
119 def ellipsisrawprocessor(rl, text):
119 def ellipsisrawprocessor(rl, text):
120 return False
120 return False
121
121
122 ellipsisprocessor = (
122 ellipsisprocessor = (
123 ellipsisreadprocessor,
123 ellipsisreadprocessor,
124 ellipsiswriteprocessor,
124 ellipsiswriteprocessor,
125 ellipsisrawprocessor,
125 ellipsisrawprocessor,
126 )
126 )
127
127
128 def addflagprocessor(flag, processor):
128 def addflagprocessor(flag, processor):
129 """Register a flag processor on a revision data flag.
129 """Register a flag processor on a revision data flag.
130
130
131 Invariant:
131 Invariant:
132 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER,
132 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER,
133 and REVIDX_RAWTEXT_CHANGING_FLAGS if they can alter rawtext.
133 and REVIDX_RAWTEXT_CHANGING_FLAGS if they can alter rawtext.
134 - Only one flag processor can be registered on a specific flag.
134 - Only one flag processor can be registered on a specific flag.
135 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
135 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
136 following signatures:
136 following signatures:
137 - (read) f(self, rawtext) -> text, bool
137 - (read) f(self, rawtext) -> text, bool
138 - (write) f(self, text) -> rawtext, bool
138 - (write) f(self, text) -> rawtext, bool
139 - (raw) f(self, rawtext) -> bool
139 - (raw) f(self, rawtext) -> bool
140 "text" is presented to the user. "rawtext" is stored in revlog data, not
140 "text" is presented to the user. "rawtext" is stored in revlog data, not
141 directly visible to the user.
141 directly visible to the user.
142 The boolean returned by these transforms is used to determine whether
142 The boolean returned by these transforms is used to determine whether
143 the returned text can be used for hash integrity checking. For example,
143 the returned text can be used for hash integrity checking. For example,
144 if "write" returns False, then "text" is used to generate hash. If
144 if "write" returns False, then "text" is used to generate hash. If
145 "write" returns True, that basically means "rawtext" returned by "write"
145 "write" returns True, that basically means "rawtext" returned by "write"
146 should be used to generate hash. Usually, "write" and "read" return
146 should be used to generate hash. Usually, "write" and "read" return
147 different booleans. And "raw" returns a same boolean as "write".
147 different booleans. And "raw" returns a same boolean as "write".
148
148
149 Note: The 'raw' transform is used for changegroup generation and in some
149 Note: The 'raw' transform is used for changegroup generation and in some
150 debug commands. In this case the transform only indicates whether the
150 debug commands. In this case the transform only indicates whether the
151 contents can be used for hash integrity checks.
151 contents can be used for hash integrity checks.
152 """
152 """
153 _insertflagprocessor(flag, processor, flagutil.flagprocessors)
153 flagutil.insertflagprocessor(flag, processor, flagutil.flagprocessors)
154
155 def _insertflagprocessor(flag, processor, flagprocessors):
156 if not flag & flagutil.REVIDX_KNOWN_FLAGS:
157 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
158 raise error.ProgrammingError(msg)
159 if flag not in REVIDX_FLAGS_ORDER:
160 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
161 raise error.ProgrammingError(msg)
162 if flag in flagprocessors:
163 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
164 raise error.Abort(msg)
165 flagprocessors[flag] = processor
166
154
167 def getoffset(q):
155 def getoffset(q):
168 return int(q >> 16)
156 return int(q >> 16)
169
157
170 def gettype(q):
158 def gettype(q):
171 return int(q & 0xFFFF)
159 return int(q & 0xFFFF)
172
160
173 def offset_type(offset, type):
161 def offset_type(offset, type):
174 if (type & ~flagutil.REVIDX_KNOWN_FLAGS) != 0:
162 if (type & ~flagutil.REVIDX_KNOWN_FLAGS) != 0:
175 raise ValueError('unknown revlog index flags')
163 raise ValueError('unknown revlog index flags')
176 return int(int(offset) << 16 | type)
164 return int(int(offset) << 16 | type)
177
165
178 @attr.s(slots=True, frozen=True)
166 @attr.s(slots=True, frozen=True)
179 class _revisioninfo(object):
167 class _revisioninfo(object):
180 """Information about a revision that allows building its fulltext
168 """Information about a revision that allows building its fulltext
181 node: expected hash of the revision
169 node: expected hash of the revision
182 p1, p2: parent revs of the revision
170 p1, p2: parent revs of the revision
183 btext: built text cache consisting of a one-element list
171 btext: built text cache consisting of a one-element list
184 cachedelta: (baserev, uncompressed_delta) or None
172 cachedelta: (baserev, uncompressed_delta) or None
185 flags: flags associated to the revision storage
173 flags: flags associated to the revision storage
186
174
187 One of btext[0] or cachedelta must be set.
175 One of btext[0] or cachedelta must be set.
188 """
176 """
189 node = attr.ib()
177 node = attr.ib()
190 p1 = attr.ib()
178 p1 = attr.ib()
191 p2 = attr.ib()
179 p2 = attr.ib()
192 btext = attr.ib()
180 btext = attr.ib()
193 textlen = attr.ib()
181 textlen = attr.ib()
194 cachedelta = attr.ib()
182 cachedelta = attr.ib()
195 flags = attr.ib()
183 flags = attr.ib()
196
184
197 @interfaceutil.implementer(repository.irevisiondelta)
185 @interfaceutil.implementer(repository.irevisiondelta)
198 @attr.s(slots=True)
186 @attr.s(slots=True)
199 class revlogrevisiondelta(object):
187 class revlogrevisiondelta(object):
200 node = attr.ib()
188 node = attr.ib()
201 p1node = attr.ib()
189 p1node = attr.ib()
202 p2node = attr.ib()
190 p2node = attr.ib()
203 basenode = attr.ib()
191 basenode = attr.ib()
204 flags = attr.ib()
192 flags = attr.ib()
205 baserevisionsize = attr.ib()
193 baserevisionsize = attr.ib()
206 revision = attr.ib()
194 revision = attr.ib()
207 delta = attr.ib()
195 delta = attr.ib()
208 linknode = attr.ib(default=None)
196 linknode = attr.ib(default=None)
209
197
210 @interfaceutil.implementer(repository.iverifyproblem)
198 @interfaceutil.implementer(repository.iverifyproblem)
211 @attr.s(frozen=True)
199 @attr.s(frozen=True)
212 class revlogproblem(object):
200 class revlogproblem(object):
213 warning = attr.ib(default=None)
201 warning = attr.ib(default=None)
214 error = attr.ib(default=None)
202 error = attr.ib(default=None)
215 node = attr.ib(default=None)
203 node = attr.ib(default=None)
216
204
217 # index v0:
205 # index v0:
218 # 4 bytes: offset
206 # 4 bytes: offset
219 # 4 bytes: compressed length
207 # 4 bytes: compressed length
220 # 4 bytes: base rev
208 # 4 bytes: base rev
221 # 4 bytes: link rev
209 # 4 bytes: link rev
222 # 20 bytes: parent 1 nodeid
210 # 20 bytes: parent 1 nodeid
223 # 20 bytes: parent 2 nodeid
211 # 20 bytes: parent 2 nodeid
224 # 20 bytes: nodeid
212 # 20 bytes: nodeid
225 indexformatv0 = struct.Struct(">4l20s20s20s")
213 indexformatv0 = struct.Struct(">4l20s20s20s")
226 indexformatv0_pack = indexformatv0.pack
214 indexformatv0_pack = indexformatv0.pack
227 indexformatv0_unpack = indexformatv0.unpack
215 indexformatv0_unpack = indexformatv0.unpack
228
216
229 class revlogoldindex(list):
217 class revlogoldindex(list):
230 def __getitem__(self, i):
218 def __getitem__(self, i):
231 if i == -1:
219 if i == -1:
232 return (0, 0, 0, -1, -1, -1, -1, nullid)
220 return (0, 0, 0, -1, -1, -1, -1, nullid)
233 return list.__getitem__(self, i)
221 return list.__getitem__(self, i)
234
222
235 class revlogoldio(object):
223 class revlogoldio(object):
236 def __init__(self):
224 def __init__(self):
237 self.size = indexformatv0.size
225 self.size = indexformatv0.size
238
226
239 def parseindex(self, data, inline):
227 def parseindex(self, data, inline):
240 s = self.size
228 s = self.size
241 index = []
229 index = []
242 nodemap = {nullid: nullrev}
230 nodemap = {nullid: nullrev}
243 n = off = 0
231 n = off = 0
244 l = len(data)
232 l = len(data)
245 while off + s <= l:
233 while off + s <= l:
246 cur = data[off:off + s]
234 cur = data[off:off + s]
247 off += s
235 off += s
248 e = indexformatv0_unpack(cur)
236 e = indexformatv0_unpack(cur)
249 # transform to revlogv1 format
237 # transform to revlogv1 format
250 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
238 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
251 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
239 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
252 index.append(e2)
240 index.append(e2)
253 nodemap[e[6]] = n
241 nodemap[e[6]] = n
254 n += 1
242 n += 1
255
243
256 return revlogoldindex(index), nodemap, None
244 return revlogoldindex(index), nodemap, None
257
245
258 def packentry(self, entry, node, version, rev):
246 def packentry(self, entry, node, version, rev):
259 if gettype(entry[0]):
247 if gettype(entry[0]):
260 raise error.RevlogError(_('index entry flags need revlog '
248 raise error.RevlogError(_('index entry flags need revlog '
261 'version 1'))
249 'version 1'))
262 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
250 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
263 node(entry[5]), node(entry[6]), entry[7])
251 node(entry[5]), node(entry[6]), entry[7])
264 return indexformatv0_pack(*e2)
252 return indexformatv0_pack(*e2)
265
253
266 # index ng:
254 # index ng:
267 # 6 bytes: offset
255 # 6 bytes: offset
268 # 2 bytes: flags
256 # 2 bytes: flags
269 # 4 bytes: compressed length
257 # 4 bytes: compressed length
270 # 4 bytes: uncompressed length
258 # 4 bytes: uncompressed length
271 # 4 bytes: base rev
259 # 4 bytes: base rev
272 # 4 bytes: link rev
260 # 4 bytes: link rev
273 # 4 bytes: parent 1 rev
261 # 4 bytes: parent 1 rev
274 # 4 bytes: parent 2 rev
262 # 4 bytes: parent 2 rev
275 # 32 bytes: nodeid
263 # 32 bytes: nodeid
276 indexformatng = struct.Struct(">Qiiiiii20s12x")
264 indexformatng = struct.Struct(">Qiiiiii20s12x")
277 indexformatng_pack = indexformatng.pack
265 indexformatng_pack = indexformatng.pack
278 versionformat = struct.Struct(">I")
266 versionformat = struct.Struct(">I")
279 versionformat_pack = versionformat.pack
267 versionformat_pack = versionformat.pack
280 versionformat_unpack = versionformat.unpack
268 versionformat_unpack = versionformat.unpack
281
269
282 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
270 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
283 # signed integer)
271 # signed integer)
284 _maxentrysize = 0x7fffffff
272 _maxentrysize = 0x7fffffff
285
273
286 class revlogio(object):
274 class revlogio(object):
287 def __init__(self):
275 def __init__(self):
288 self.size = indexformatng.size
276 self.size = indexformatng.size
289
277
290 def parseindex(self, data, inline):
278 def parseindex(self, data, inline):
291 # call the C implementation to parse the index data
279 # call the C implementation to parse the index data
292 index, cache = parsers.parse_index2(data, inline)
280 index, cache = parsers.parse_index2(data, inline)
293 return index, getattr(index, 'nodemap', None), cache
281 return index, getattr(index, 'nodemap', None), cache
294
282
295 def packentry(self, entry, node, version, rev):
283 def packentry(self, entry, node, version, rev):
296 p = indexformatng_pack(*entry)
284 p = indexformatng_pack(*entry)
297 if rev == 0:
285 if rev == 0:
298 p = versionformat_pack(version) + p[4:]
286 p = versionformat_pack(version) + p[4:]
299 return p
287 return p
300
288
301 class revlog(object):
289 class revlog(object):
302 """
290 """
303 the underlying revision storage object
291 the underlying revision storage object
304
292
305 A revlog consists of two parts, an index and the revision data.
293 A revlog consists of two parts, an index and the revision data.
306
294
307 The index is a file with a fixed record size containing
295 The index is a file with a fixed record size containing
308 information on each revision, including its nodeid (hash), the
296 information on each revision, including its nodeid (hash), the
309 nodeids of its parents, the position and offset of its data within
297 nodeids of its parents, the position and offset of its data within
310 the data file, and the revision it's based on. Finally, each entry
298 the data file, and the revision it's based on. Finally, each entry
311 contains a linkrev entry that can serve as a pointer to external
299 contains a linkrev entry that can serve as a pointer to external
312 data.
300 data.
313
301
314 The revision data itself is a linear collection of data chunks.
302 The revision data itself is a linear collection of data chunks.
315 Each chunk represents a revision and is usually represented as a
303 Each chunk represents a revision and is usually represented as a
316 delta against the previous chunk. To bound lookup time, runs of
304 delta against the previous chunk. To bound lookup time, runs of
317 deltas are limited to about 2 times the length of the original
305 deltas are limited to about 2 times the length of the original
318 version data. This makes retrieval of a version proportional to
306 version data. This makes retrieval of a version proportional to
319 its size, or O(1) relative to the number of revisions.
307 its size, or O(1) relative to the number of revisions.
320
308
321 Both pieces of the revlog are written to in an append-only
309 Both pieces of the revlog are written to in an append-only
322 fashion, which means we never need to rewrite a file to insert or
310 fashion, which means we never need to rewrite a file to insert or
323 remove data, and can use some simple techniques to avoid the need
311 remove data, and can use some simple techniques to avoid the need
324 for locking while reading.
312 for locking while reading.
325
313
326 If checkambig, indexfile is opened with checkambig=True at
314 If checkambig, indexfile is opened with checkambig=True at
327 writing, to avoid file stat ambiguity.
315 writing, to avoid file stat ambiguity.
328
316
329 If mmaplargeindex is True, and an mmapindexthreshold is set, the
317 If mmaplargeindex is True, and an mmapindexthreshold is set, the
330 index will be mmapped rather than read if it is larger than the
318 index will be mmapped rather than read if it is larger than the
331 configured threshold.
319 configured threshold.
332
320
333 If censorable is True, the revlog can have censored revisions.
321 If censorable is True, the revlog can have censored revisions.
334
322
335 If `upperboundcomp` is not None, this is the expected maximal gain from
323 If `upperboundcomp` is not None, this is the expected maximal gain from
336 compression for the data content.
324 compression for the data content.
337 """
325 """
338 def __init__(self, opener, indexfile, datafile=None, checkambig=False,
326 def __init__(self, opener, indexfile, datafile=None, checkambig=False,
339 mmaplargeindex=False, censorable=False,
327 mmaplargeindex=False, censorable=False,
340 upperboundcomp=None):
328 upperboundcomp=None):
341 """
329 """
342 create a revlog object
330 create a revlog object
343
331
344 opener is a function that abstracts the file opening operation
332 opener is a function that abstracts the file opening operation
345 and can be used to implement COW semantics or the like.
333 and can be used to implement COW semantics or the like.
346
334
347 """
335 """
348 self.upperboundcomp = upperboundcomp
336 self.upperboundcomp = upperboundcomp
349 self.indexfile = indexfile
337 self.indexfile = indexfile
350 self.datafile = datafile or (indexfile[:-2] + ".d")
338 self.datafile = datafile or (indexfile[:-2] + ".d")
351 self.opener = opener
339 self.opener = opener
352 # When True, indexfile is opened with checkambig=True at writing, to
340 # When True, indexfile is opened with checkambig=True at writing, to
353 # avoid file stat ambiguity.
341 # avoid file stat ambiguity.
354 self._checkambig = checkambig
342 self._checkambig = checkambig
355 self._mmaplargeindex = mmaplargeindex
343 self._mmaplargeindex = mmaplargeindex
356 self._censorable = censorable
344 self._censorable = censorable
357 # 3-tuple of (node, rev, text) for a raw revision.
345 # 3-tuple of (node, rev, text) for a raw revision.
358 self._revisioncache = None
346 self._revisioncache = None
359 # Maps rev to chain base rev.
347 # Maps rev to chain base rev.
360 self._chainbasecache = util.lrucachedict(100)
348 self._chainbasecache = util.lrucachedict(100)
361 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
349 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
362 self._chunkcache = (0, '')
350 self._chunkcache = (0, '')
363 # How much data to read and cache into the raw revlog data cache.
351 # How much data to read and cache into the raw revlog data cache.
364 self._chunkcachesize = 65536
352 self._chunkcachesize = 65536
365 self._maxchainlen = None
353 self._maxchainlen = None
366 self._deltabothparents = True
354 self._deltabothparents = True
367 self.index = []
355 self.index = []
368 # Mapping of partial identifiers to full nodes.
356 # Mapping of partial identifiers to full nodes.
369 self._pcache = {}
357 self._pcache = {}
370 # Mapping of revision integer to full node.
358 # Mapping of revision integer to full node.
371 self._nodecache = {nullid: nullrev}
359 self._nodecache = {nullid: nullrev}
372 self._nodepos = None
360 self._nodepos = None
373 self._compengine = 'zlib'
361 self._compengine = 'zlib'
374 self._compengineopts = {}
362 self._compengineopts = {}
375 self._maxdeltachainspan = -1
363 self._maxdeltachainspan = -1
376 self._withsparseread = False
364 self._withsparseread = False
377 self._sparserevlog = False
365 self._sparserevlog = False
378 self._srdensitythreshold = 0.50
366 self._srdensitythreshold = 0.50
379 self._srmingapsize = 262144
367 self._srmingapsize = 262144
380
368
381 # Make copy of flag processors so each revlog instance can support
369 # Make copy of flag processors so each revlog instance can support
382 # custom flags.
370 # custom flags.
383 self._flagprocessors = dict(flagutil.flagprocessors)
371 self._flagprocessors = dict(flagutil.flagprocessors)
384
372
385 # 2-tuple of file handles being used for active writing.
373 # 2-tuple of file handles being used for active writing.
386 self._writinghandles = None
374 self._writinghandles = None
387
375
388 self._loadindex()
376 self._loadindex()
389
377
390 def _loadindex(self):
378 def _loadindex(self):
391 mmapindexthreshold = None
379 mmapindexthreshold = None
392 opts = getattr(self.opener, 'options', {}) or {}
380 opts = getattr(self.opener, 'options', {}) or {}
393
381
394 if 'revlogv2' in opts:
382 if 'revlogv2' in opts:
395 newversionflags = REVLOGV2 | FLAG_INLINE_DATA
383 newversionflags = REVLOGV2 | FLAG_INLINE_DATA
396 elif 'revlogv1' in opts:
384 elif 'revlogv1' in opts:
397 newversionflags = REVLOGV1 | FLAG_INLINE_DATA
385 newversionflags = REVLOGV1 | FLAG_INLINE_DATA
398 if 'generaldelta' in opts:
386 if 'generaldelta' in opts:
399 newversionflags |= FLAG_GENERALDELTA
387 newversionflags |= FLAG_GENERALDELTA
400 elif getattr(self.opener, 'options', None) is not None:
388 elif getattr(self.opener, 'options', None) is not None:
401 # If options provided but no 'revlog*' found, the repository
389 # If options provided but no 'revlog*' found, the repository
402 # would have no 'requires' file in it, which means we have to
390 # would have no 'requires' file in it, which means we have to
403 # stick to the old format.
391 # stick to the old format.
404 newversionflags = REVLOGV0
392 newversionflags = REVLOGV0
405 else:
393 else:
406 newversionflags = REVLOG_DEFAULT_VERSION
394 newversionflags = REVLOG_DEFAULT_VERSION
407
395
408 if 'chunkcachesize' in opts:
396 if 'chunkcachesize' in opts:
409 self._chunkcachesize = opts['chunkcachesize']
397 self._chunkcachesize = opts['chunkcachesize']
410 if 'maxchainlen' in opts:
398 if 'maxchainlen' in opts:
411 self._maxchainlen = opts['maxchainlen']
399 self._maxchainlen = opts['maxchainlen']
412 if 'deltabothparents' in opts:
400 if 'deltabothparents' in opts:
413 self._deltabothparents = opts['deltabothparents']
401 self._deltabothparents = opts['deltabothparents']
414 self._lazydelta = bool(opts.get('lazydelta', True))
402 self._lazydelta = bool(opts.get('lazydelta', True))
415 self._lazydeltabase = False
403 self._lazydeltabase = False
416 if self._lazydelta:
404 if self._lazydelta:
417 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
405 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
418 if 'compengine' in opts:
406 if 'compengine' in opts:
419 self._compengine = opts['compengine']
407 self._compengine = opts['compengine']
420 if 'zlib.level' in opts:
408 if 'zlib.level' in opts:
421 self._compengineopts['zlib.level'] = opts['zlib.level']
409 self._compengineopts['zlib.level'] = opts['zlib.level']
422 if 'zstd.level' in opts:
410 if 'zstd.level' in opts:
423 self._compengineopts['zstd.level'] = opts['zstd.level']
411 self._compengineopts['zstd.level'] = opts['zstd.level']
424 if 'maxdeltachainspan' in opts:
412 if 'maxdeltachainspan' in opts:
425 self._maxdeltachainspan = opts['maxdeltachainspan']
413 self._maxdeltachainspan = opts['maxdeltachainspan']
426 if self._mmaplargeindex and 'mmapindexthreshold' in opts:
414 if self._mmaplargeindex and 'mmapindexthreshold' in opts:
427 mmapindexthreshold = opts['mmapindexthreshold']
415 mmapindexthreshold = opts['mmapindexthreshold']
428 self._sparserevlog = bool(opts.get('sparse-revlog', False))
416 self._sparserevlog = bool(opts.get('sparse-revlog', False))
429 withsparseread = bool(opts.get('with-sparse-read', False))
417 withsparseread = bool(opts.get('with-sparse-read', False))
430 # sparse-revlog forces sparse-read
418 # sparse-revlog forces sparse-read
431 self._withsparseread = self._sparserevlog or withsparseread
419 self._withsparseread = self._sparserevlog or withsparseread
432 if 'sparse-read-density-threshold' in opts:
420 if 'sparse-read-density-threshold' in opts:
433 self._srdensitythreshold = opts['sparse-read-density-threshold']
421 self._srdensitythreshold = opts['sparse-read-density-threshold']
434 if 'sparse-read-min-gap-size' in opts:
422 if 'sparse-read-min-gap-size' in opts:
435 self._srmingapsize = opts['sparse-read-min-gap-size']
423 self._srmingapsize = opts['sparse-read-min-gap-size']
436 if opts.get('enableellipsis'):
424 if opts.get('enableellipsis'):
437 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor
425 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor
438
426
439 # revlog v0 doesn't have flag processors
427 # revlog v0 doesn't have flag processors
440 for flag, processor in opts.get(b'flagprocessors', {}).iteritems():
428 for flag, processor in opts.get(b'flagprocessors', {}).iteritems():
441 _insertflagprocessor(flag, processor, self._flagprocessors)
429 flagutil.insertflagprocessor(flag, processor, self._flagprocessors)
442
430
443 if self._chunkcachesize <= 0:
431 if self._chunkcachesize <= 0:
444 raise error.RevlogError(_('revlog chunk cache size %r is not '
432 raise error.RevlogError(_('revlog chunk cache size %r is not '
445 'greater than 0') % self._chunkcachesize)
433 'greater than 0') % self._chunkcachesize)
446 elif self._chunkcachesize & (self._chunkcachesize - 1):
434 elif self._chunkcachesize & (self._chunkcachesize - 1):
447 raise error.RevlogError(_('revlog chunk cache size %r is not a '
435 raise error.RevlogError(_('revlog chunk cache size %r is not a '
448 'power of 2') % self._chunkcachesize)
436 'power of 2') % self._chunkcachesize)
449
437
450 indexdata = ''
438 indexdata = ''
451 self._initempty = True
439 self._initempty = True
452 try:
440 try:
453 with self._indexfp() as f:
441 with self._indexfp() as f:
454 if (mmapindexthreshold is not None and
442 if (mmapindexthreshold is not None and
455 self.opener.fstat(f).st_size >= mmapindexthreshold):
443 self.opener.fstat(f).st_size >= mmapindexthreshold):
456 # TODO: should .close() to release resources without
444 # TODO: should .close() to release resources without
457 # relying on Python GC
445 # relying on Python GC
458 indexdata = util.buffer(util.mmapread(f))
446 indexdata = util.buffer(util.mmapread(f))
459 else:
447 else:
460 indexdata = f.read()
448 indexdata = f.read()
461 if len(indexdata) > 0:
449 if len(indexdata) > 0:
462 versionflags = versionformat_unpack(indexdata[:4])[0]
450 versionflags = versionformat_unpack(indexdata[:4])[0]
463 self._initempty = False
451 self._initempty = False
464 else:
452 else:
465 versionflags = newversionflags
453 versionflags = newversionflags
466 except IOError as inst:
454 except IOError as inst:
467 if inst.errno != errno.ENOENT:
455 if inst.errno != errno.ENOENT:
468 raise
456 raise
469
457
470 versionflags = newversionflags
458 versionflags = newversionflags
471
459
472 self.version = versionflags
460 self.version = versionflags
473
461
474 flags = versionflags & ~0xFFFF
462 flags = versionflags & ~0xFFFF
475 fmt = versionflags & 0xFFFF
463 fmt = versionflags & 0xFFFF
476
464
477 if fmt == REVLOGV0:
465 if fmt == REVLOGV0:
478 if flags:
466 if flags:
479 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
467 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
480 'revlog %s') %
468 'revlog %s') %
481 (flags >> 16, fmt, self.indexfile))
469 (flags >> 16, fmt, self.indexfile))
482
470
483 self._inline = False
471 self._inline = False
484 self._generaldelta = False
472 self._generaldelta = False
485
473
486 elif fmt == REVLOGV1:
474 elif fmt == REVLOGV1:
487 if flags & ~REVLOGV1_FLAGS:
475 if flags & ~REVLOGV1_FLAGS:
488 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
476 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
489 'revlog %s') %
477 'revlog %s') %
490 (flags >> 16, fmt, self.indexfile))
478 (flags >> 16, fmt, self.indexfile))
491
479
492 self._inline = versionflags & FLAG_INLINE_DATA
480 self._inline = versionflags & FLAG_INLINE_DATA
493 self._generaldelta = versionflags & FLAG_GENERALDELTA
481 self._generaldelta = versionflags & FLAG_GENERALDELTA
494
482
495 elif fmt == REVLOGV2:
483 elif fmt == REVLOGV2:
496 if flags & ~REVLOGV2_FLAGS:
484 if flags & ~REVLOGV2_FLAGS:
497 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
485 raise error.RevlogError(_('unknown flags (%#04x) in version %d '
498 'revlog %s') %
486 'revlog %s') %
499 (flags >> 16, fmt, self.indexfile))
487 (flags >> 16, fmt, self.indexfile))
500
488
501 self._inline = versionflags & FLAG_INLINE_DATA
489 self._inline = versionflags & FLAG_INLINE_DATA
502 # generaldelta implied by version 2 revlogs.
490 # generaldelta implied by version 2 revlogs.
503 self._generaldelta = True
491 self._generaldelta = True
504
492
505 else:
493 else:
506 raise error.RevlogError(_('unknown version (%d) in revlog %s') %
494 raise error.RevlogError(_('unknown version (%d) in revlog %s') %
507 (fmt, self.indexfile))
495 (fmt, self.indexfile))
508 # sparse-revlog can't be on without general-delta (issue6056)
496 # sparse-revlog can't be on without general-delta (issue6056)
509 if not self._generaldelta:
497 if not self._generaldelta:
510 self._sparserevlog = False
498 self._sparserevlog = False
511
499
512 self._storedeltachains = True
500 self._storedeltachains = True
513
501
514 self._io = revlogio()
502 self._io = revlogio()
515 if self.version == REVLOGV0:
503 if self.version == REVLOGV0:
516 self._io = revlogoldio()
504 self._io = revlogoldio()
517 try:
505 try:
518 d = self._io.parseindex(indexdata, self._inline)
506 d = self._io.parseindex(indexdata, self._inline)
519 except (ValueError, IndexError):
507 except (ValueError, IndexError):
520 raise error.RevlogError(_("index %s is corrupted") %
508 raise error.RevlogError(_("index %s is corrupted") %
521 self.indexfile)
509 self.indexfile)
522 self.index, nodemap, self._chunkcache = d
510 self.index, nodemap, self._chunkcache = d
523 if nodemap is not None:
511 if nodemap is not None:
524 self.nodemap = self._nodecache = nodemap
512 self.nodemap = self._nodecache = nodemap
525 if not self._chunkcache:
513 if not self._chunkcache:
526 self._chunkclear()
514 self._chunkclear()
527 # revnum -> (chain-length, sum-delta-length)
515 # revnum -> (chain-length, sum-delta-length)
528 self._chaininfocache = {}
516 self._chaininfocache = {}
529 # revlog header -> revlog compressor
517 # revlog header -> revlog compressor
530 self._decompressors = {}
518 self._decompressors = {}
531
519
532 @util.propertycache
520 @util.propertycache
533 def _compressor(self):
521 def _compressor(self):
534 engine = util.compengines[self._compengine]
522 engine = util.compengines[self._compengine]
535 return engine.revlogcompressor(self._compengineopts)
523 return engine.revlogcompressor(self._compengineopts)
536
524
537 def _indexfp(self, mode='r'):
525 def _indexfp(self, mode='r'):
538 """file object for the revlog's index file"""
526 """file object for the revlog's index file"""
539 args = {r'mode': mode}
527 args = {r'mode': mode}
540 if mode != 'r':
528 if mode != 'r':
541 args[r'checkambig'] = self._checkambig
529 args[r'checkambig'] = self._checkambig
542 if mode == 'w':
530 if mode == 'w':
543 args[r'atomictemp'] = True
531 args[r'atomictemp'] = True
544 return self.opener(self.indexfile, **args)
532 return self.opener(self.indexfile, **args)
545
533
546 def _datafp(self, mode='r'):
534 def _datafp(self, mode='r'):
547 """file object for the revlog's data file"""
535 """file object for the revlog's data file"""
548 return self.opener(self.datafile, mode=mode)
536 return self.opener(self.datafile, mode=mode)
549
537
550 @contextlib.contextmanager
538 @contextlib.contextmanager
551 def _datareadfp(self, existingfp=None):
539 def _datareadfp(self, existingfp=None):
552 """file object suitable to read data"""
540 """file object suitable to read data"""
553 # Use explicit file handle, if given.
541 # Use explicit file handle, if given.
554 if existingfp is not None:
542 if existingfp is not None:
555 yield existingfp
543 yield existingfp
556
544
557 # Use a file handle being actively used for writes, if available.
545 # Use a file handle being actively used for writes, if available.
558 # There is some danger to doing this because reads will seek the
546 # There is some danger to doing this because reads will seek the
559 # file. However, _writeentry() performs a SEEK_END before all writes,
547 # file. However, _writeentry() performs a SEEK_END before all writes,
560 # so we should be safe.
548 # so we should be safe.
561 elif self._writinghandles:
549 elif self._writinghandles:
562 if self._inline:
550 if self._inline:
563 yield self._writinghandles[0]
551 yield self._writinghandles[0]
564 else:
552 else:
565 yield self._writinghandles[1]
553 yield self._writinghandles[1]
566
554
567 # Otherwise open a new file handle.
555 # Otherwise open a new file handle.
568 else:
556 else:
569 if self._inline:
557 if self._inline:
570 func = self._indexfp
558 func = self._indexfp
571 else:
559 else:
572 func = self._datafp
560 func = self._datafp
573 with func() as fp:
561 with func() as fp:
574 yield fp
562 yield fp
575
563
576 def tip(self):
564 def tip(self):
577 return self.node(len(self.index) - 1)
565 return self.node(len(self.index) - 1)
578 def __contains__(self, rev):
566 def __contains__(self, rev):
579 return 0 <= rev < len(self)
567 return 0 <= rev < len(self)
580 def __len__(self):
568 def __len__(self):
581 return len(self.index)
569 return len(self.index)
582 def __iter__(self):
570 def __iter__(self):
583 return iter(pycompat.xrange(len(self)))
571 return iter(pycompat.xrange(len(self)))
584 def revs(self, start=0, stop=None):
572 def revs(self, start=0, stop=None):
585 """iterate over all rev in this revlog (from start to stop)"""
573 """iterate over all rev in this revlog (from start to stop)"""
586 return storageutil.iterrevs(len(self), start=start, stop=stop)
574 return storageutil.iterrevs(len(self), start=start, stop=stop)
587
575
588 @util.propertycache
576 @util.propertycache
589 def nodemap(self):
577 def nodemap(self):
590 if self.index:
578 if self.index:
591 # populate mapping down to the initial node
579 # populate mapping down to the initial node
592 node0 = self.index[0][7] # get around changelog filtering
580 node0 = self.index[0][7] # get around changelog filtering
593 self.rev(node0)
581 self.rev(node0)
594 return self._nodecache
582 return self._nodecache
595
583
596 def hasnode(self, node):
584 def hasnode(self, node):
597 try:
585 try:
598 self.rev(node)
586 self.rev(node)
599 return True
587 return True
600 except KeyError:
588 except KeyError:
601 return False
589 return False
602
590
603 def candelta(self, baserev, rev):
591 def candelta(self, baserev, rev):
604 """whether two revisions (baserev, rev) can be delta-ed or not"""
592 """whether two revisions (baserev, rev) can be delta-ed or not"""
605 # Disable delta if either rev requires a content-changing flag
593 # Disable delta if either rev requires a content-changing flag
606 # processor (ex. LFS). This is because such flag processor can alter
594 # processor (ex. LFS). This is because such flag processor can alter
607 # the rawtext content that the delta will be based on, and two clients
595 # the rawtext content that the delta will be based on, and two clients
608 # could have a same revlog node with different flags (i.e. different
596 # could have a same revlog node with different flags (i.e. different
609 # rawtext contents) and the delta could be incompatible.
597 # rawtext contents) and the delta could be incompatible.
610 if ((self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS)
598 if ((self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS)
611 or (self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS)):
599 or (self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS)):
612 return False
600 return False
613 return True
601 return True
614
602
615 def clearcaches(self):
603 def clearcaches(self):
616 self._revisioncache = None
604 self._revisioncache = None
617 self._chainbasecache.clear()
605 self._chainbasecache.clear()
618 self._chunkcache = (0, '')
606 self._chunkcache = (0, '')
619 self._pcache = {}
607 self._pcache = {}
620
608
621 try:
609 try:
622 # If we are using the native C version, you are in a fun case
610 # If we are using the native C version, you are in a fun case
623 # where self.index, self.nodemap and self._nodecaches is the same
611 # where self.index, self.nodemap and self._nodecaches is the same
624 # object.
612 # object.
625 self._nodecache.clearcaches()
613 self._nodecache.clearcaches()
626 except AttributeError:
614 except AttributeError:
627 self._nodecache = {nullid: nullrev}
615 self._nodecache = {nullid: nullrev}
628 self._nodepos = None
616 self._nodepos = None
629
617
630 def rev(self, node):
618 def rev(self, node):
631 try:
619 try:
632 return self._nodecache[node]
620 return self._nodecache[node]
633 except TypeError:
621 except TypeError:
634 raise
622 raise
635 except error.RevlogError:
623 except error.RevlogError:
636 # parsers.c radix tree lookup failed
624 # parsers.c radix tree lookup failed
637 if node == wdirid or node in wdirfilenodeids:
625 if node == wdirid or node in wdirfilenodeids:
638 raise error.WdirUnsupported
626 raise error.WdirUnsupported
639 raise error.LookupError(node, self.indexfile, _('no node'))
627 raise error.LookupError(node, self.indexfile, _('no node'))
640 except KeyError:
628 except KeyError:
641 # pure python cache lookup failed
629 # pure python cache lookup failed
642 n = self._nodecache
630 n = self._nodecache
643 i = self.index
631 i = self.index
644 p = self._nodepos
632 p = self._nodepos
645 if p is None:
633 if p is None:
646 p = len(i) - 1
634 p = len(i) - 1
647 else:
635 else:
648 assert p < len(i)
636 assert p < len(i)
649 for r in pycompat.xrange(p, -1, -1):
637 for r in pycompat.xrange(p, -1, -1):
650 v = i[r][7]
638 v = i[r][7]
651 n[v] = r
639 n[v] = r
652 if v == node:
640 if v == node:
653 self._nodepos = r - 1
641 self._nodepos = r - 1
654 return r
642 return r
655 if node == wdirid or node in wdirfilenodeids:
643 if node == wdirid or node in wdirfilenodeids:
656 raise error.WdirUnsupported
644 raise error.WdirUnsupported
657 raise error.LookupError(node, self.indexfile, _('no node'))
645 raise error.LookupError(node, self.indexfile, _('no node'))
658
646
659 # Accessors for index entries.
647 # Accessors for index entries.
660
648
661 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
649 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
662 # are flags.
650 # are flags.
663 def start(self, rev):
651 def start(self, rev):
664 return int(self.index[rev][0] >> 16)
652 return int(self.index[rev][0] >> 16)
665
653
666 def flags(self, rev):
654 def flags(self, rev):
667 return self.index[rev][0] & 0xFFFF
655 return self.index[rev][0] & 0xFFFF
668
656
669 def length(self, rev):
657 def length(self, rev):
670 return self.index[rev][1]
658 return self.index[rev][1]
671
659
672 def rawsize(self, rev):
660 def rawsize(self, rev):
673 """return the length of the uncompressed text for a given revision"""
661 """return the length of the uncompressed text for a given revision"""
674 l = self.index[rev][2]
662 l = self.index[rev][2]
675 if l >= 0:
663 if l >= 0:
676 return l
664 return l
677
665
678 t = self.revision(rev, raw=True)
666 t = self.revision(rev, raw=True)
679 return len(t)
667 return len(t)
680
668
681 def size(self, rev):
669 def size(self, rev):
682 """length of non-raw text (processed by a "read" flag processor)"""
670 """length of non-raw text (processed by a "read" flag processor)"""
683 # fast path: if no "read" flag processor could change the content,
671 # fast path: if no "read" flag processor could change the content,
684 # size is rawsize. note: ELLIPSIS is known to not change the content.
672 # size is rawsize. note: ELLIPSIS is known to not change the content.
685 flags = self.flags(rev)
673 flags = self.flags(rev)
686 if flags & (flagutil.REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
674 if flags & (flagutil.REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
687 return self.rawsize(rev)
675 return self.rawsize(rev)
688
676
689 return len(self.revision(rev, raw=False))
677 return len(self.revision(rev, raw=False))
690
678
691 def chainbase(self, rev):
679 def chainbase(self, rev):
692 base = self._chainbasecache.get(rev)
680 base = self._chainbasecache.get(rev)
693 if base is not None:
681 if base is not None:
694 return base
682 return base
695
683
696 index = self.index
684 index = self.index
697 iterrev = rev
685 iterrev = rev
698 base = index[iterrev][3]
686 base = index[iterrev][3]
699 while base != iterrev:
687 while base != iterrev:
700 iterrev = base
688 iterrev = base
701 base = index[iterrev][3]
689 base = index[iterrev][3]
702
690
703 self._chainbasecache[rev] = base
691 self._chainbasecache[rev] = base
704 return base
692 return base
705
693
706 def linkrev(self, rev):
694 def linkrev(self, rev):
707 return self.index[rev][4]
695 return self.index[rev][4]
708
696
709 def parentrevs(self, rev):
697 def parentrevs(self, rev):
710 try:
698 try:
711 entry = self.index[rev]
699 entry = self.index[rev]
712 except IndexError:
700 except IndexError:
713 if rev == wdirrev:
701 if rev == wdirrev:
714 raise error.WdirUnsupported
702 raise error.WdirUnsupported
715 raise
703 raise
716
704
717 return entry[5], entry[6]
705 return entry[5], entry[6]
718
706
719 # fast parentrevs(rev) where rev isn't filtered
707 # fast parentrevs(rev) where rev isn't filtered
720 _uncheckedparentrevs = parentrevs
708 _uncheckedparentrevs = parentrevs
721
709
722 def node(self, rev):
710 def node(self, rev):
723 try:
711 try:
724 return self.index[rev][7]
712 return self.index[rev][7]
725 except IndexError:
713 except IndexError:
726 if rev == wdirrev:
714 if rev == wdirrev:
727 raise error.WdirUnsupported
715 raise error.WdirUnsupported
728 raise
716 raise
729
717
730 # Derived from index values.
718 # Derived from index values.
731
719
732 def end(self, rev):
720 def end(self, rev):
733 return self.start(rev) + self.length(rev)
721 return self.start(rev) + self.length(rev)
734
722
735 def parents(self, node):
723 def parents(self, node):
736 i = self.index
724 i = self.index
737 d = i[self.rev(node)]
725 d = i[self.rev(node)]
738 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
726 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
739
727
740 def chainlen(self, rev):
728 def chainlen(self, rev):
741 return self._chaininfo(rev)[0]
729 return self._chaininfo(rev)[0]
742
730
743 def _chaininfo(self, rev):
731 def _chaininfo(self, rev):
744 chaininfocache = self._chaininfocache
732 chaininfocache = self._chaininfocache
745 if rev in chaininfocache:
733 if rev in chaininfocache:
746 return chaininfocache[rev]
734 return chaininfocache[rev]
747 index = self.index
735 index = self.index
748 generaldelta = self._generaldelta
736 generaldelta = self._generaldelta
749 iterrev = rev
737 iterrev = rev
750 e = index[iterrev]
738 e = index[iterrev]
751 clen = 0
739 clen = 0
752 compresseddeltalen = 0
740 compresseddeltalen = 0
753 while iterrev != e[3]:
741 while iterrev != e[3]:
754 clen += 1
742 clen += 1
755 compresseddeltalen += e[1]
743 compresseddeltalen += e[1]
756 if generaldelta:
744 if generaldelta:
757 iterrev = e[3]
745 iterrev = e[3]
758 else:
746 else:
759 iterrev -= 1
747 iterrev -= 1
760 if iterrev in chaininfocache:
748 if iterrev in chaininfocache:
761 t = chaininfocache[iterrev]
749 t = chaininfocache[iterrev]
762 clen += t[0]
750 clen += t[0]
763 compresseddeltalen += t[1]
751 compresseddeltalen += t[1]
764 break
752 break
765 e = index[iterrev]
753 e = index[iterrev]
766 else:
754 else:
767 # Add text length of base since decompressing that also takes
755 # Add text length of base since decompressing that also takes
768 # work. For cache hits the length is already included.
756 # work. For cache hits the length is already included.
769 compresseddeltalen += e[1]
757 compresseddeltalen += e[1]
770 r = (clen, compresseddeltalen)
758 r = (clen, compresseddeltalen)
771 chaininfocache[rev] = r
759 chaininfocache[rev] = r
772 return r
760 return r
773
761
774 def _deltachain(self, rev, stoprev=None):
762 def _deltachain(self, rev, stoprev=None):
775 """Obtain the delta chain for a revision.
763 """Obtain the delta chain for a revision.
776
764
777 ``stoprev`` specifies a revision to stop at. If not specified, we
765 ``stoprev`` specifies a revision to stop at. If not specified, we
778 stop at the base of the chain.
766 stop at the base of the chain.
779
767
780 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
768 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
781 revs in ascending order and ``stopped`` is a bool indicating whether
769 revs in ascending order and ``stopped`` is a bool indicating whether
782 ``stoprev`` was hit.
770 ``stoprev`` was hit.
783 """
771 """
784 # Try C implementation.
772 # Try C implementation.
785 try:
773 try:
786 return self.index.deltachain(rev, stoprev, self._generaldelta)
774 return self.index.deltachain(rev, stoprev, self._generaldelta)
787 except AttributeError:
775 except AttributeError:
788 pass
776 pass
789
777
790 chain = []
778 chain = []
791
779
792 # Alias to prevent attribute lookup in tight loop.
780 # Alias to prevent attribute lookup in tight loop.
793 index = self.index
781 index = self.index
794 generaldelta = self._generaldelta
782 generaldelta = self._generaldelta
795
783
796 iterrev = rev
784 iterrev = rev
797 e = index[iterrev]
785 e = index[iterrev]
798 while iterrev != e[3] and iterrev != stoprev:
786 while iterrev != e[3] and iterrev != stoprev:
799 chain.append(iterrev)
787 chain.append(iterrev)
800 if generaldelta:
788 if generaldelta:
801 iterrev = e[3]
789 iterrev = e[3]
802 else:
790 else:
803 iterrev -= 1
791 iterrev -= 1
804 e = index[iterrev]
792 e = index[iterrev]
805
793
806 if iterrev == stoprev:
794 if iterrev == stoprev:
807 stopped = True
795 stopped = True
808 else:
796 else:
809 chain.append(iterrev)
797 chain.append(iterrev)
810 stopped = False
798 stopped = False
811
799
812 chain.reverse()
800 chain.reverse()
813 return chain, stopped
801 return chain, stopped
814
802
815 def ancestors(self, revs, stoprev=0, inclusive=False):
803 def ancestors(self, revs, stoprev=0, inclusive=False):
816 """Generate the ancestors of 'revs' in reverse revision order.
804 """Generate the ancestors of 'revs' in reverse revision order.
817 Does not generate revs lower than stoprev.
805 Does not generate revs lower than stoprev.
818
806
819 See the documentation for ancestor.lazyancestors for more details."""
807 See the documentation for ancestor.lazyancestors for more details."""
820
808
821 # first, make sure start revisions aren't filtered
809 # first, make sure start revisions aren't filtered
822 revs = list(revs)
810 revs = list(revs)
823 checkrev = self.node
811 checkrev = self.node
824 for r in revs:
812 for r in revs:
825 checkrev(r)
813 checkrev(r)
826 # and we're sure ancestors aren't filtered as well
814 # and we're sure ancestors aren't filtered as well
827
815
828 if rustancestor is not None:
816 if rustancestor is not None:
829 lazyancestors = rustancestor.LazyAncestors
817 lazyancestors = rustancestor.LazyAncestors
830 arg = self.index
818 arg = self.index
831 elif util.safehasattr(parsers, 'rustlazyancestors'):
819 elif util.safehasattr(parsers, 'rustlazyancestors'):
832 lazyancestors = ancestor.rustlazyancestors
820 lazyancestors = ancestor.rustlazyancestors
833 arg = self.index
821 arg = self.index
834 else:
822 else:
835 lazyancestors = ancestor.lazyancestors
823 lazyancestors = ancestor.lazyancestors
836 arg = self._uncheckedparentrevs
824 arg = self._uncheckedparentrevs
837 return lazyancestors(arg, revs, stoprev=stoprev, inclusive=inclusive)
825 return lazyancestors(arg, revs, stoprev=stoprev, inclusive=inclusive)
838
826
839 def descendants(self, revs):
827 def descendants(self, revs):
840 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
828 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
841
829
842 def findcommonmissing(self, common=None, heads=None):
830 def findcommonmissing(self, common=None, heads=None):
843 """Return a tuple of the ancestors of common and the ancestors of heads
831 """Return a tuple of the ancestors of common and the ancestors of heads
844 that are not ancestors of common. In revset terminology, we return the
832 that are not ancestors of common. In revset terminology, we return the
845 tuple:
833 tuple:
846
834
847 ::common, (::heads) - (::common)
835 ::common, (::heads) - (::common)
848
836
849 The list is sorted by revision number, meaning it is
837 The list is sorted by revision number, meaning it is
850 topologically sorted.
838 topologically sorted.
851
839
852 'heads' and 'common' are both lists of node IDs. If heads is
840 'heads' and 'common' are both lists of node IDs. If heads is
853 not supplied, uses all of the revlog's heads. If common is not
841 not supplied, uses all of the revlog's heads. If common is not
854 supplied, uses nullid."""
842 supplied, uses nullid."""
855 if common is None:
843 if common is None:
856 common = [nullid]
844 common = [nullid]
857 if heads is None:
845 if heads is None:
858 heads = self.heads()
846 heads = self.heads()
859
847
860 common = [self.rev(n) for n in common]
848 common = [self.rev(n) for n in common]
861 heads = [self.rev(n) for n in heads]
849 heads = [self.rev(n) for n in heads]
862
850
863 # we want the ancestors, but inclusive
851 # we want the ancestors, but inclusive
864 class lazyset(object):
852 class lazyset(object):
865 def __init__(self, lazyvalues):
853 def __init__(self, lazyvalues):
866 self.addedvalues = set()
854 self.addedvalues = set()
867 self.lazyvalues = lazyvalues
855 self.lazyvalues = lazyvalues
868
856
869 def __contains__(self, value):
857 def __contains__(self, value):
870 return value in self.addedvalues or value in self.lazyvalues
858 return value in self.addedvalues or value in self.lazyvalues
871
859
872 def __iter__(self):
860 def __iter__(self):
873 added = self.addedvalues
861 added = self.addedvalues
874 for r in added:
862 for r in added:
875 yield r
863 yield r
876 for r in self.lazyvalues:
864 for r in self.lazyvalues:
877 if not r in added:
865 if not r in added:
878 yield r
866 yield r
879
867
880 def add(self, value):
868 def add(self, value):
881 self.addedvalues.add(value)
869 self.addedvalues.add(value)
882
870
883 def update(self, values):
871 def update(self, values):
884 self.addedvalues.update(values)
872 self.addedvalues.update(values)
885
873
886 has = lazyset(self.ancestors(common))
874 has = lazyset(self.ancestors(common))
887 has.add(nullrev)
875 has.add(nullrev)
888 has.update(common)
876 has.update(common)
889
877
890 # take all ancestors from heads that aren't in has
878 # take all ancestors from heads that aren't in has
891 missing = set()
879 missing = set()
892 visit = collections.deque(r for r in heads if r not in has)
880 visit = collections.deque(r for r in heads if r not in has)
893 while visit:
881 while visit:
894 r = visit.popleft()
882 r = visit.popleft()
895 if r in missing:
883 if r in missing:
896 continue
884 continue
897 else:
885 else:
898 missing.add(r)
886 missing.add(r)
899 for p in self.parentrevs(r):
887 for p in self.parentrevs(r):
900 if p not in has:
888 if p not in has:
901 visit.append(p)
889 visit.append(p)
902 missing = list(missing)
890 missing = list(missing)
903 missing.sort()
891 missing.sort()
904 return has, [self.node(miss) for miss in missing]
892 return has, [self.node(miss) for miss in missing]
905
893
906 def incrementalmissingrevs(self, common=None):
894 def incrementalmissingrevs(self, common=None):
907 """Return an object that can be used to incrementally compute the
895 """Return an object that can be used to incrementally compute the
908 revision numbers of the ancestors of arbitrary sets that are not
896 revision numbers of the ancestors of arbitrary sets that are not
909 ancestors of common. This is an ancestor.incrementalmissingancestors
897 ancestors of common. This is an ancestor.incrementalmissingancestors
910 object.
898 object.
911
899
912 'common' is a list of revision numbers. If common is not supplied, uses
900 'common' is a list of revision numbers. If common is not supplied, uses
913 nullrev.
901 nullrev.
914 """
902 """
915 if common is None:
903 if common is None:
916 common = [nullrev]
904 common = [nullrev]
917
905
918 if rustancestor is not None:
906 if rustancestor is not None:
919 return rustancestor.MissingAncestors(self.index, common)
907 return rustancestor.MissingAncestors(self.index, common)
920 return ancestor.incrementalmissingancestors(self.parentrevs, common)
908 return ancestor.incrementalmissingancestors(self.parentrevs, common)
921
909
922 def findmissingrevs(self, common=None, heads=None):
910 def findmissingrevs(self, common=None, heads=None):
923 """Return the revision numbers of the ancestors of heads that
911 """Return the revision numbers of the ancestors of heads that
924 are not ancestors of common.
912 are not ancestors of common.
925
913
926 More specifically, return a list of revision numbers corresponding to
914 More specifically, return a list of revision numbers corresponding to
927 nodes N such that every N satisfies the following constraints:
915 nodes N such that every N satisfies the following constraints:
928
916
929 1. N is an ancestor of some node in 'heads'
917 1. N is an ancestor of some node in 'heads'
930 2. N is not an ancestor of any node in 'common'
918 2. N is not an ancestor of any node in 'common'
931
919
932 The list is sorted by revision number, meaning it is
920 The list is sorted by revision number, meaning it is
933 topologically sorted.
921 topologically sorted.
934
922
935 'heads' and 'common' are both lists of revision numbers. If heads is
923 'heads' and 'common' are both lists of revision numbers. If heads is
936 not supplied, uses all of the revlog's heads. If common is not
924 not supplied, uses all of the revlog's heads. If common is not
937 supplied, uses nullid."""
925 supplied, uses nullid."""
938 if common is None:
926 if common is None:
939 common = [nullrev]
927 common = [nullrev]
940 if heads is None:
928 if heads is None:
941 heads = self.headrevs()
929 heads = self.headrevs()
942
930
943 inc = self.incrementalmissingrevs(common=common)
931 inc = self.incrementalmissingrevs(common=common)
944 return inc.missingancestors(heads)
932 return inc.missingancestors(heads)
945
933
946 def findmissing(self, common=None, heads=None):
934 def findmissing(self, common=None, heads=None):
947 """Return the ancestors of heads that are not ancestors of common.
935 """Return the ancestors of heads that are not ancestors of common.
948
936
949 More specifically, return a list of nodes N such that every N
937 More specifically, return a list of nodes N such that every N
950 satisfies the following constraints:
938 satisfies the following constraints:
951
939
952 1. N is an ancestor of some node in 'heads'
940 1. N is an ancestor of some node in 'heads'
953 2. N is not an ancestor of any node in 'common'
941 2. N is not an ancestor of any node in 'common'
954
942
955 The list is sorted by revision number, meaning it is
943 The list is sorted by revision number, meaning it is
956 topologically sorted.
944 topologically sorted.
957
945
958 'heads' and 'common' are both lists of node IDs. If heads is
946 'heads' and 'common' are both lists of node IDs. If heads is
959 not supplied, uses all of the revlog's heads. If common is not
947 not supplied, uses all of the revlog's heads. If common is not
960 supplied, uses nullid."""
948 supplied, uses nullid."""
961 if common is None:
949 if common is None:
962 common = [nullid]
950 common = [nullid]
963 if heads is None:
951 if heads is None:
964 heads = self.heads()
952 heads = self.heads()
965
953
966 common = [self.rev(n) for n in common]
954 common = [self.rev(n) for n in common]
967 heads = [self.rev(n) for n in heads]
955 heads = [self.rev(n) for n in heads]
968
956
969 inc = self.incrementalmissingrevs(common=common)
957 inc = self.incrementalmissingrevs(common=common)
970 return [self.node(r) for r in inc.missingancestors(heads)]
958 return [self.node(r) for r in inc.missingancestors(heads)]
971
959
972 def nodesbetween(self, roots=None, heads=None):
960 def nodesbetween(self, roots=None, heads=None):
973 """Return a topological path from 'roots' to 'heads'.
961 """Return a topological path from 'roots' to 'heads'.
974
962
975 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
963 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
976 topologically sorted list of all nodes N that satisfy both of
964 topologically sorted list of all nodes N that satisfy both of
977 these constraints:
965 these constraints:
978
966
979 1. N is a descendant of some node in 'roots'
967 1. N is a descendant of some node in 'roots'
980 2. N is an ancestor of some node in 'heads'
968 2. N is an ancestor of some node in 'heads'
981
969
982 Every node is considered to be both a descendant and an ancestor
970 Every node is considered to be both a descendant and an ancestor
983 of itself, so every reachable node in 'roots' and 'heads' will be
971 of itself, so every reachable node in 'roots' and 'heads' will be
984 included in 'nodes'.
972 included in 'nodes'.
985
973
986 'outroots' is the list of reachable nodes in 'roots', i.e., the
974 'outroots' is the list of reachable nodes in 'roots', i.e., the
987 subset of 'roots' that is returned in 'nodes'. Likewise,
975 subset of 'roots' that is returned in 'nodes'. Likewise,
988 'outheads' is the subset of 'heads' that is also in 'nodes'.
976 'outheads' is the subset of 'heads' that is also in 'nodes'.
989
977
990 'roots' and 'heads' are both lists of node IDs. If 'roots' is
978 'roots' and 'heads' are both lists of node IDs. If 'roots' is
991 unspecified, uses nullid as the only root. If 'heads' is
979 unspecified, uses nullid as the only root. If 'heads' is
992 unspecified, uses list of all of the revlog's heads."""
980 unspecified, uses list of all of the revlog's heads."""
993 nonodes = ([], [], [])
981 nonodes = ([], [], [])
994 if roots is not None:
982 if roots is not None:
995 roots = list(roots)
983 roots = list(roots)
996 if not roots:
984 if not roots:
997 return nonodes
985 return nonodes
998 lowestrev = min([self.rev(n) for n in roots])
986 lowestrev = min([self.rev(n) for n in roots])
999 else:
987 else:
1000 roots = [nullid] # Everybody's a descendant of nullid
988 roots = [nullid] # Everybody's a descendant of nullid
1001 lowestrev = nullrev
989 lowestrev = nullrev
1002 if (lowestrev == nullrev) and (heads is None):
990 if (lowestrev == nullrev) and (heads is None):
1003 # We want _all_ the nodes!
991 # We want _all_ the nodes!
1004 return ([self.node(r) for r in self], [nullid], list(self.heads()))
992 return ([self.node(r) for r in self], [nullid], list(self.heads()))
1005 if heads is None:
993 if heads is None:
1006 # All nodes are ancestors, so the latest ancestor is the last
994 # All nodes are ancestors, so the latest ancestor is the last
1007 # node.
995 # node.
1008 highestrev = len(self) - 1
996 highestrev = len(self) - 1
1009 # Set ancestors to None to signal that every node is an ancestor.
997 # Set ancestors to None to signal that every node is an ancestor.
1010 ancestors = None
998 ancestors = None
1011 # Set heads to an empty dictionary for later discovery of heads
999 # Set heads to an empty dictionary for later discovery of heads
1012 heads = {}
1000 heads = {}
1013 else:
1001 else:
1014 heads = list(heads)
1002 heads = list(heads)
1015 if not heads:
1003 if not heads:
1016 return nonodes
1004 return nonodes
1017 ancestors = set()
1005 ancestors = set()
1018 # Turn heads into a dictionary so we can remove 'fake' heads.
1006 # Turn heads into a dictionary so we can remove 'fake' heads.
1019 # Also, later we will be using it to filter out the heads we can't
1007 # Also, later we will be using it to filter out the heads we can't
1020 # find from roots.
1008 # find from roots.
1021 heads = dict.fromkeys(heads, False)
1009 heads = dict.fromkeys(heads, False)
1022 # Start at the top and keep marking parents until we're done.
1010 # Start at the top and keep marking parents until we're done.
1023 nodestotag = set(heads)
1011 nodestotag = set(heads)
1024 # Remember where the top was so we can use it as a limit later.
1012 # Remember where the top was so we can use it as a limit later.
1025 highestrev = max([self.rev(n) for n in nodestotag])
1013 highestrev = max([self.rev(n) for n in nodestotag])
1026 while nodestotag:
1014 while nodestotag:
1027 # grab a node to tag
1015 # grab a node to tag
1028 n = nodestotag.pop()
1016 n = nodestotag.pop()
1029 # Never tag nullid
1017 # Never tag nullid
1030 if n == nullid:
1018 if n == nullid:
1031 continue
1019 continue
1032 # A node's revision number represents its place in a
1020 # A node's revision number represents its place in a
1033 # topologically sorted list of nodes.
1021 # topologically sorted list of nodes.
1034 r = self.rev(n)
1022 r = self.rev(n)
1035 if r >= lowestrev:
1023 if r >= lowestrev:
1036 if n not in ancestors:
1024 if n not in ancestors:
1037 # If we are possibly a descendant of one of the roots
1025 # If we are possibly a descendant of one of the roots
1038 # and we haven't already been marked as an ancestor
1026 # and we haven't already been marked as an ancestor
1039 ancestors.add(n) # Mark as ancestor
1027 ancestors.add(n) # Mark as ancestor
1040 # Add non-nullid parents to list of nodes to tag.
1028 # Add non-nullid parents to list of nodes to tag.
1041 nodestotag.update([p for p in self.parents(n) if
1029 nodestotag.update([p for p in self.parents(n) if
1042 p != nullid])
1030 p != nullid])
1043 elif n in heads: # We've seen it before, is it a fake head?
1031 elif n in heads: # We've seen it before, is it a fake head?
1044 # So it is, real heads should not be the ancestors of
1032 # So it is, real heads should not be the ancestors of
1045 # any other heads.
1033 # any other heads.
1046 heads.pop(n)
1034 heads.pop(n)
1047 if not ancestors:
1035 if not ancestors:
1048 return nonodes
1036 return nonodes
1049 # Now that we have our set of ancestors, we want to remove any
1037 # Now that we have our set of ancestors, we want to remove any
1050 # roots that are not ancestors.
1038 # roots that are not ancestors.
1051
1039
1052 # If one of the roots was nullid, everything is included anyway.
1040 # If one of the roots was nullid, everything is included anyway.
1053 if lowestrev > nullrev:
1041 if lowestrev > nullrev:
1054 # But, since we weren't, let's recompute the lowest rev to not
1042 # But, since we weren't, let's recompute the lowest rev to not
1055 # include roots that aren't ancestors.
1043 # include roots that aren't ancestors.
1056
1044
1057 # Filter out roots that aren't ancestors of heads
1045 # Filter out roots that aren't ancestors of heads
1058 roots = [root for root in roots if root in ancestors]
1046 roots = [root for root in roots if root in ancestors]
1059 # Recompute the lowest revision
1047 # Recompute the lowest revision
1060 if roots:
1048 if roots:
1061 lowestrev = min([self.rev(root) for root in roots])
1049 lowestrev = min([self.rev(root) for root in roots])
1062 else:
1050 else:
1063 # No more roots? Return empty list
1051 # No more roots? Return empty list
1064 return nonodes
1052 return nonodes
1065 else:
1053 else:
1066 # We are descending from nullid, and don't need to care about
1054 # We are descending from nullid, and don't need to care about
1067 # any other roots.
1055 # any other roots.
1068 lowestrev = nullrev
1056 lowestrev = nullrev
1069 roots = [nullid]
1057 roots = [nullid]
1070 # Transform our roots list into a set.
1058 # Transform our roots list into a set.
1071 descendants = set(roots)
1059 descendants = set(roots)
1072 # Also, keep the original roots so we can filter out roots that aren't
1060 # Also, keep the original roots so we can filter out roots that aren't
1073 # 'real' roots (i.e. are descended from other roots).
1061 # 'real' roots (i.e. are descended from other roots).
1074 roots = descendants.copy()
1062 roots = descendants.copy()
1075 # Our topologically sorted list of output nodes.
1063 # Our topologically sorted list of output nodes.
1076 orderedout = []
1064 orderedout = []
1077 # Don't start at nullid since we don't want nullid in our output list,
1065 # Don't start at nullid since we don't want nullid in our output list,
1078 # and if nullid shows up in descendants, empty parents will look like
1066 # and if nullid shows up in descendants, empty parents will look like
1079 # they're descendants.
1067 # they're descendants.
1080 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1068 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1081 n = self.node(r)
1069 n = self.node(r)
1082 isdescendant = False
1070 isdescendant = False
1083 if lowestrev == nullrev: # Everybody is a descendant of nullid
1071 if lowestrev == nullrev: # Everybody is a descendant of nullid
1084 isdescendant = True
1072 isdescendant = True
1085 elif n in descendants:
1073 elif n in descendants:
1086 # n is already a descendant
1074 # n is already a descendant
1087 isdescendant = True
1075 isdescendant = True
1088 # This check only needs to be done here because all the roots
1076 # This check only needs to be done here because all the roots
1089 # will start being marked is descendants before the loop.
1077 # will start being marked is descendants before the loop.
1090 if n in roots:
1078 if n in roots:
1091 # If n was a root, check if it's a 'real' root.
1079 # If n was a root, check if it's a 'real' root.
1092 p = tuple(self.parents(n))
1080 p = tuple(self.parents(n))
1093 # If any of its parents are descendants, it's not a root.
1081 # If any of its parents are descendants, it's not a root.
1094 if (p[0] in descendants) or (p[1] in descendants):
1082 if (p[0] in descendants) or (p[1] in descendants):
1095 roots.remove(n)
1083 roots.remove(n)
1096 else:
1084 else:
1097 p = tuple(self.parents(n))
1085 p = tuple(self.parents(n))
1098 # A node is a descendant if either of its parents are
1086 # A node is a descendant if either of its parents are
1099 # descendants. (We seeded the dependents list with the roots
1087 # descendants. (We seeded the dependents list with the roots
1100 # up there, remember?)
1088 # up there, remember?)
1101 if (p[0] in descendants) or (p[1] in descendants):
1089 if (p[0] in descendants) or (p[1] in descendants):
1102 descendants.add(n)
1090 descendants.add(n)
1103 isdescendant = True
1091 isdescendant = True
1104 if isdescendant and ((ancestors is None) or (n in ancestors)):
1092 if isdescendant and ((ancestors is None) or (n in ancestors)):
1105 # Only include nodes that are both descendants and ancestors.
1093 # Only include nodes that are both descendants and ancestors.
1106 orderedout.append(n)
1094 orderedout.append(n)
1107 if (ancestors is not None) and (n in heads):
1095 if (ancestors is not None) and (n in heads):
1108 # We're trying to figure out which heads are reachable
1096 # We're trying to figure out which heads are reachable
1109 # from roots.
1097 # from roots.
1110 # Mark this head as having been reached
1098 # Mark this head as having been reached
1111 heads[n] = True
1099 heads[n] = True
1112 elif ancestors is None:
1100 elif ancestors is None:
1113 # Otherwise, we're trying to discover the heads.
1101 # Otherwise, we're trying to discover the heads.
1114 # Assume this is a head because if it isn't, the next step
1102 # Assume this is a head because if it isn't, the next step
1115 # will eventually remove it.
1103 # will eventually remove it.
1116 heads[n] = True
1104 heads[n] = True
1117 # But, obviously its parents aren't.
1105 # But, obviously its parents aren't.
1118 for p in self.parents(n):
1106 for p in self.parents(n):
1119 heads.pop(p, None)
1107 heads.pop(p, None)
1120 heads = [head for head, flag in heads.iteritems() if flag]
1108 heads = [head for head, flag in heads.iteritems() if flag]
1121 roots = list(roots)
1109 roots = list(roots)
1122 assert orderedout
1110 assert orderedout
1123 assert roots
1111 assert roots
1124 assert heads
1112 assert heads
1125 return (orderedout, roots, heads)
1113 return (orderedout, roots, heads)
1126
1114
1127 def headrevs(self, revs=None):
1115 def headrevs(self, revs=None):
1128 if revs is None:
1116 if revs is None:
1129 try:
1117 try:
1130 return self.index.headrevs()
1118 return self.index.headrevs()
1131 except AttributeError:
1119 except AttributeError:
1132 return self._headrevs()
1120 return self._headrevs()
1133 if rustdagop is not None:
1121 if rustdagop is not None:
1134 return rustdagop.headrevs(self.index, revs)
1122 return rustdagop.headrevs(self.index, revs)
1135 return dagop.headrevs(revs, self._uncheckedparentrevs)
1123 return dagop.headrevs(revs, self._uncheckedparentrevs)
1136
1124
1137 def computephases(self, roots):
1125 def computephases(self, roots):
1138 return self.index.computephasesmapsets(roots)
1126 return self.index.computephasesmapsets(roots)
1139
1127
1140 def _headrevs(self):
1128 def _headrevs(self):
1141 count = len(self)
1129 count = len(self)
1142 if not count:
1130 if not count:
1143 return [nullrev]
1131 return [nullrev]
1144 # we won't iter over filtered rev so nobody is a head at start
1132 # we won't iter over filtered rev so nobody is a head at start
1145 ishead = [0] * (count + 1)
1133 ishead = [0] * (count + 1)
1146 index = self.index
1134 index = self.index
1147 for r in self:
1135 for r in self:
1148 ishead[r] = 1 # I may be an head
1136 ishead[r] = 1 # I may be an head
1149 e = index[r]
1137 e = index[r]
1150 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1138 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1151 return [r for r, val in enumerate(ishead) if val]
1139 return [r for r, val in enumerate(ishead) if val]
1152
1140
1153 def heads(self, start=None, stop=None):
1141 def heads(self, start=None, stop=None):
1154 """return the list of all nodes that have no children
1142 """return the list of all nodes that have no children
1155
1143
1156 if start is specified, only heads that are descendants of
1144 if start is specified, only heads that are descendants of
1157 start will be returned
1145 start will be returned
1158 if stop is specified, it will consider all the revs from stop
1146 if stop is specified, it will consider all the revs from stop
1159 as if they had no children
1147 as if they had no children
1160 """
1148 """
1161 if start is None and stop is None:
1149 if start is None and stop is None:
1162 if not len(self):
1150 if not len(self):
1163 return [nullid]
1151 return [nullid]
1164 return [self.node(r) for r in self.headrevs()]
1152 return [self.node(r) for r in self.headrevs()]
1165
1153
1166 if start is None:
1154 if start is None:
1167 start = nullrev
1155 start = nullrev
1168 else:
1156 else:
1169 start = self.rev(start)
1157 start = self.rev(start)
1170
1158
1171 stoprevs = set(self.rev(n) for n in stop or [])
1159 stoprevs = set(self.rev(n) for n in stop or [])
1172
1160
1173 revs = dagop.headrevssubset(self.revs, self.parentrevs, startrev=start,
1161 revs = dagop.headrevssubset(self.revs, self.parentrevs, startrev=start,
1174 stoprevs=stoprevs)
1162 stoprevs=stoprevs)
1175
1163
1176 return [self.node(rev) for rev in revs]
1164 return [self.node(rev) for rev in revs]
1177
1165
1178 def children(self, node):
1166 def children(self, node):
1179 """find the children of a given node"""
1167 """find the children of a given node"""
1180 c = []
1168 c = []
1181 p = self.rev(node)
1169 p = self.rev(node)
1182 for r in self.revs(start=p + 1):
1170 for r in self.revs(start=p + 1):
1183 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1171 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1184 if prevs:
1172 if prevs:
1185 for pr in prevs:
1173 for pr in prevs:
1186 if pr == p:
1174 if pr == p:
1187 c.append(self.node(r))
1175 c.append(self.node(r))
1188 elif p == nullrev:
1176 elif p == nullrev:
1189 c.append(self.node(r))
1177 c.append(self.node(r))
1190 return c
1178 return c
1191
1179
1192 def commonancestorsheads(self, a, b):
1180 def commonancestorsheads(self, a, b):
1193 """calculate all the heads of the common ancestors of nodes a and b"""
1181 """calculate all the heads of the common ancestors of nodes a and b"""
1194 a, b = self.rev(a), self.rev(b)
1182 a, b = self.rev(a), self.rev(b)
1195 ancs = self._commonancestorsheads(a, b)
1183 ancs = self._commonancestorsheads(a, b)
1196 return pycompat.maplist(self.node, ancs)
1184 return pycompat.maplist(self.node, ancs)
1197
1185
1198 def _commonancestorsheads(self, *revs):
1186 def _commonancestorsheads(self, *revs):
1199 """calculate all the heads of the common ancestors of revs"""
1187 """calculate all the heads of the common ancestors of revs"""
1200 try:
1188 try:
1201 ancs = self.index.commonancestorsheads(*revs)
1189 ancs = self.index.commonancestorsheads(*revs)
1202 except (AttributeError, OverflowError): # C implementation failed
1190 except (AttributeError, OverflowError): # C implementation failed
1203 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1191 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1204 return ancs
1192 return ancs
1205
1193
1206 def isancestor(self, a, b):
1194 def isancestor(self, a, b):
1207 """return True if node a is an ancestor of node b
1195 """return True if node a is an ancestor of node b
1208
1196
1209 A revision is considered an ancestor of itself."""
1197 A revision is considered an ancestor of itself."""
1210 a, b = self.rev(a), self.rev(b)
1198 a, b = self.rev(a), self.rev(b)
1211 return self.isancestorrev(a, b)
1199 return self.isancestorrev(a, b)
1212
1200
1213 def isancestorrev(self, a, b):
1201 def isancestorrev(self, a, b):
1214 """return True if revision a is an ancestor of revision b
1202 """return True if revision a is an ancestor of revision b
1215
1203
1216 A revision is considered an ancestor of itself.
1204 A revision is considered an ancestor of itself.
1217
1205
1218 The implementation of this is trivial but the use of
1206 The implementation of this is trivial but the use of
1219 reachableroots is not."""
1207 reachableroots is not."""
1220 if a == nullrev:
1208 if a == nullrev:
1221 return True
1209 return True
1222 elif a == b:
1210 elif a == b:
1223 return True
1211 return True
1224 elif a > b:
1212 elif a > b:
1225 return False
1213 return False
1226 return bool(self.reachableroots(a, [b], [a], includepath=False))
1214 return bool(self.reachableroots(a, [b], [a], includepath=False))
1227
1215
1228 def reachableroots(self, minroot, heads, roots, includepath=False):
1216 def reachableroots(self, minroot, heads, roots, includepath=False):
1229 """return (heads(::<roots> and <roots>::<heads>))
1217 """return (heads(::<roots> and <roots>::<heads>))
1230
1218
1231 If includepath is True, return (<roots>::<heads>)."""
1219 If includepath is True, return (<roots>::<heads>)."""
1232 try:
1220 try:
1233 return self.index.reachableroots2(minroot, heads, roots,
1221 return self.index.reachableroots2(minroot, heads, roots,
1234 includepath)
1222 includepath)
1235 except AttributeError:
1223 except AttributeError:
1236 return dagop._reachablerootspure(self.parentrevs,
1224 return dagop._reachablerootspure(self.parentrevs,
1237 minroot, roots, heads, includepath)
1225 minroot, roots, heads, includepath)
1238
1226
1239 def ancestor(self, a, b):
1227 def ancestor(self, a, b):
1240 """calculate the "best" common ancestor of nodes a and b"""
1228 """calculate the "best" common ancestor of nodes a and b"""
1241
1229
1242 a, b = self.rev(a), self.rev(b)
1230 a, b = self.rev(a), self.rev(b)
1243 try:
1231 try:
1244 ancs = self.index.ancestors(a, b)
1232 ancs = self.index.ancestors(a, b)
1245 except (AttributeError, OverflowError):
1233 except (AttributeError, OverflowError):
1246 ancs = ancestor.ancestors(self.parentrevs, a, b)
1234 ancs = ancestor.ancestors(self.parentrevs, a, b)
1247 if ancs:
1235 if ancs:
1248 # choose a consistent winner when there's a tie
1236 # choose a consistent winner when there's a tie
1249 return min(map(self.node, ancs))
1237 return min(map(self.node, ancs))
1250 return nullid
1238 return nullid
1251
1239
1252 def _match(self, id):
1240 def _match(self, id):
1253 if isinstance(id, int):
1241 if isinstance(id, int):
1254 # rev
1242 # rev
1255 return self.node(id)
1243 return self.node(id)
1256 if len(id) == 20:
1244 if len(id) == 20:
1257 # possibly a binary node
1245 # possibly a binary node
1258 # odds of a binary node being all hex in ASCII are 1 in 10**25
1246 # odds of a binary node being all hex in ASCII are 1 in 10**25
1259 try:
1247 try:
1260 node = id
1248 node = id
1261 self.rev(node) # quick search the index
1249 self.rev(node) # quick search the index
1262 return node
1250 return node
1263 except error.LookupError:
1251 except error.LookupError:
1264 pass # may be partial hex id
1252 pass # may be partial hex id
1265 try:
1253 try:
1266 # str(rev)
1254 # str(rev)
1267 rev = int(id)
1255 rev = int(id)
1268 if "%d" % rev != id:
1256 if "%d" % rev != id:
1269 raise ValueError
1257 raise ValueError
1270 if rev < 0:
1258 if rev < 0:
1271 rev = len(self) + rev
1259 rev = len(self) + rev
1272 if rev < 0 or rev >= len(self):
1260 if rev < 0 or rev >= len(self):
1273 raise ValueError
1261 raise ValueError
1274 return self.node(rev)
1262 return self.node(rev)
1275 except (ValueError, OverflowError):
1263 except (ValueError, OverflowError):
1276 pass
1264 pass
1277 if len(id) == 40:
1265 if len(id) == 40:
1278 try:
1266 try:
1279 # a full hex nodeid?
1267 # a full hex nodeid?
1280 node = bin(id)
1268 node = bin(id)
1281 self.rev(node)
1269 self.rev(node)
1282 return node
1270 return node
1283 except (TypeError, error.LookupError):
1271 except (TypeError, error.LookupError):
1284 pass
1272 pass
1285
1273
1286 def _partialmatch(self, id):
1274 def _partialmatch(self, id):
1287 # we don't care wdirfilenodeids as they should be always full hash
1275 # we don't care wdirfilenodeids as they should be always full hash
1288 maybewdir = wdirhex.startswith(id)
1276 maybewdir = wdirhex.startswith(id)
1289 try:
1277 try:
1290 partial = self.index.partialmatch(id)
1278 partial = self.index.partialmatch(id)
1291 if partial and self.hasnode(partial):
1279 if partial and self.hasnode(partial):
1292 if maybewdir:
1280 if maybewdir:
1293 # single 'ff...' match in radix tree, ambiguous with wdir
1281 # single 'ff...' match in radix tree, ambiguous with wdir
1294 raise error.RevlogError
1282 raise error.RevlogError
1295 return partial
1283 return partial
1296 if maybewdir:
1284 if maybewdir:
1297 # no 'ff...' match in radix tree, wdir identified
1285 # no 'ff...' match in radix tree, wdir identified
1298 raise error.WdirUnsupported
1286 raise error.WdirUnsupported
1299 return None
1287 return None
1300 except error.RevlogError:
1288 except error.RevlogError:
1301 # parsers.c radix tree lookup gave multiple matches
1289 # parsers.c radix tree lookup gave multiple matches
1302 # fast path: for unfiltered changelog, radix tree is accurate
1290 # fast path: for unfiltered changelog, radix tree is accurate
1303 if not getattr(self, 'filteredrevs', None):
1291 if not getattr(self, 'filteredrevs', None):
1304 raise error.AmbiguousPrefixLookupError(
1292 raise error.AmbiguousPrefixLookupError(
1305 id, self.indexfile, _('ambiguous identifier'))
1293 id, self.indexfile, _('ambiguous identifier'))
1306 # fall through to slow path that filters hidden revisions
1294 # fall through to slow path that filters hidden revisions
1307 except (AttributeError, ValueError):
1295 except (AttributeError, ValueError):
1308 # we are pure python, or key was too short to search radix tree
1296 # we are pure python, or key was too short to search radix tree
1309 pass
1297 pass
1310
1298
1311 if id in self._pcache:
1299 if id in self._pcache:
1312 return self._pcache[id]
1300 return self._pcache[id]
1313
1301
1314 if len(id) <= 40:
1302 if len(id) <= 40:
1315 try:
1303 try:
1316 # hex(node)[:...]
1304 # hex(node)[:...]
1317 l = len(id) // 2 # grab an even number of digits
1305 l = len(id) // 2 # grab an even number of digits
1318 prefix = bin(id[:l * 2])
1306 prefix = bin(id[:l * 2])
1319 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1307 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1320 nl = [n for n in nl if hex(n).startswith(id) and
1308 nl = [n for n in nl if hex(n).startswith(id) and
1321 self.hasnode(n)]
1309 self.hasnode(n)]
1322 if nullhex.startswith(id):
1310 if nullhex.startswith(id):
1323 nl.append(nullid)
1311 nl.append(nullid)
1324 if len(nl) > 0:
1312 if len(nl) > 0:
1325 if len(nl) == 1 and not maybewdir:
1313 if len(nl) == 1 and not maybewdir:
1326 self._pcache[id] = nl[0]
1314 self._pcache[id] = nl[0]
1327 return nl[0]
1315 return nl[0]
1328 raise error.AmbiguousPrefixLookupError(
1316 raise error.AmbiguousPrefixLookupError(
1329 id, self.indexfile, _('ambiguous identifier'))
1317 id, self.indexfile, _('ambiguous identifier'))
1330 if maybewdir:
1318 if maybewdir:
1331 raise error.WdirUnsupported
1319 raise error.WdirUnsupported
1332 return None
1320 return None
1333 except TypeError:
1321 except TypeError:
1334 pass
1322 pass
1335
1323
1336 def lookup(self, id):
1324 def lookup(self, id):
1337 """locate a node based on:
1325 """locate a node based on:
1338 - revision number or str(revision number)
1326 - revision number or str(revision number)
1339 - nodeid or subset of hex nodeid
1327 - nodeid or subset of hex nodeid
1340 """
1328 """
1341 n = self._match(id)
1329 n = self._match(id)
1342 if n is not None:
1330 if n is not None:
1343 return n
1331 return n
1344 n = self._partialmatch(id)
1332 n = self._partialmatch(id)
1345 if n:
1333 if n:
1346 return n
1334 return n
1347
1335
1348 raise error.LookupError(id, self.indexfile, _('no match found'))
1336 raise error.LookupError(id, self.indexfile, _('no match found'))
1349
1337
1350 def shortest(self, node, minlength=1):
1338 def shortest(self, node, minlength=1):
1351 """Find the shortest unambiguous prefix that matches node."""
1339 """Find the shortest unambiguous prefix that matches node."""
1352 def isvalid(prefix):
1340 def isvalid(prefix):
1353 try:
1341 try:
1354 matchednode = self._partialmatch(prefix)
1342 matchednode = self._partialmatch(prefix)
1355 except error.AmbiguousPrefixLookupError:
1343 except error.AmbiguousPrefixLookupError:
1356 return False
1344 return False
1357 except error.WdirUnsupported:
1345 except error.WdirUnsupported:
1358 # single 'ff...' match
1346 # single 'ff...' match
1359 return True
1347 return True
1360 if matchednode is None:
1348 if matchednode is None:
1361 raise error.LookupError(node, self.indexfile, _('no node'))
1349 raise error.LookupError(node, self.indexfile, _('no node'))
1362 return True
1350 return True
1363
1351
1364 def maybewdir(prefix):
1352 def maybewdir(prefix):
1365 return all(c == 'f' for c in pycompat.iterbytestr(prefix))
1353 return all(c == 'f' for c in pycompat.iterbytestr(prefix))
1366
1354
1367 hexnode = hex(node)
1355 hexnode = hex(node)
1368
1356
1369 def disambiguate(hexnode, minlength):
1357 def disambiguate(hexnode, minlength):
1370 """Disambiguate against wdirid."""
1358 """Disambiguate against wdirid."""
1371 for length in range(minlength, 41):
1359 for length in range(minlength, 41):
1372 prefix = hexnode[:length]
1360 prefix = hexnode[:length]
1373 if not maybewdir(prefix):
1361 if not maybewdir(prefix):
1374 return prefix
1362 return prefix
1375
1363
1376 if not getattr(self, 'filteredrevs', None):
1364 if not getattr(self, 'filteredrevs', None):
1377 try:
1365 try:
1378 length = max(self.index.shortest(node), minlength)
1366 length = max(self.index.shortest(node), minlength)
1379 return disambiguate(hexnode, length)
1367 return disambiguate(hexnode, length)
1380 except error.RevlogError:
1368 except error.RevlogError:
1381 if node != wdirid:
1369 if node != wdirid:
1382 raise error.LookupError(node, self.indexfile, _('no node'))
1370 raise error.LookupError(node, self.indexfile, _('no node'))
1383 except AttributeError:
1371 except AttributeError:
1384 # Fall through to pure code
1372 # Fall through to pure code
1385 pass
1373 pass
1386
1374
1387 if node == wdirid:
1375 if node == wdirid:
1388 for length in range(minlength, 41):
1376 for length in range(minlength, 41):
1389 prefix = hexnode[:length]
1377 prefix = hexnode[:length]
1390 if isvalid(prefix):
1378 if isvalid(prefix):
1391 return prefix
1379 return prefix
1392
1380
1393 for length in range(minlength, 41):
1381 for length in range(minlength, 41):
1394 prefix = hexnode[:length]
1382 prefix = hexnode[:length]
1395 if isvalid(prefix):
1383 if isvalid(prefix):
1396 return disambiguate(hexnode, length)
1384 return disambiguate(hexnode, length)
1397
1385
1398 def cmp(self, node, text):
1386 def cmp(self, node, text):
1399 """compare text with a given file revision
1387 """compare text with a given file revision
1400
1388
1401 returns True if text is different than what is stored.
1389 returns True if text is different than what is stored.
1402 """
1390 """
1403 p1, p2 = self.parents(node)
1391 p1, p2 = self.parents(node)
1404 return storageutil.hashrevisionsha1(text, p1, p2) != node
1392 return storageutil.hashrevisionsha1(text, p1, p2) != node
1405
1393
1406 def _cachesegment(self, offset, data):
1394 def _cachesegment(self, offset, data):
1407 """Add a segment to the revlog cache.
1395 """Add a segment to the revlog cache.
1408
1396
1409 Accepts an absolute offset and the data that is at that location.
1397 Accepts an absolute offset and the data that is at that location.
1410 """
1398 """
1411 o, d = self._chunkcache
1399 o, d = self._chunkcache
1412 # try to add to existing cache
1400 # try to add to existing cache
1413 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1401 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1414 self._chunkcache = o, d + data
1402 self._chunkcache = o, d + data
1415 else:
1403 else:
1416 self._chunkcache = offset, data
1404 self._chunkcache = offset, data
1417
1405
1418 def _readsegment(self, offset, length, df=None):
1406 def _readsegment(self, offset, length, df=None):
1419 """Load a segment of raw data from the revlog.
1407 """Load a segment of raw data from the revlog.
1420
1408
1421 Accepts an absolute offset, length to read, and an optional existing
1409 Accepts an absolute offset, length to read, and an optional existing
1422 file handle to read from.
1410 file handle to read from.
1423
1411
1424 If an existing file handle is passed, it will be seeked and the
1412 If an existing file handle is passed, it will be seeked and the
1425 original seek position will NOT be restored.
1413 original seek position will NOT be restored.
1426
1414
1427 Returns a str or buffer of raw byte data.
1415 Returns a str or buffer of raw byte data.
1428
1416
1429 Raises if the requested number of bytes could not be read.
1417 Raises if the requested number of bytes could not be read.
1430 """
1418 """
1431 # Cache data both forward and backward around the requested
1419 # Cache data both forward and backward around the requested
1432 # data, in a fixed size window. This helps speed up operations
1420 # data, in a fixed size window. This helps speed up operations
1433 # involving reading the revlog backwards.
1421 # involving reading the revlog backwards.
1434 cachesize = self._chunkcachesize
1422 cachesize = self._chunkcachesize
1435 realoffset = offset & ~(cachesize - 1)
1423 realoffset = offset & ~(cachesize - 1)
1436 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1424 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1437 - realoffset)
1425 - realoffset)
1438 with self._datareadfp(df) as df:
1426 with self._datareadfp(df) as df:
1439 df.seek(realoffset)
1427 df.seek(realoffset)
1440 d = df.read(reallength)
1428 d = df.read(reallength)
1441
1429
1442 self._cachesegment(realoffset, d)
1430 self._cachesegment(realoffset, d)
1443 if offset != realoffset or reallength != length:
1431 if offset != realoffset or reallength != length:
1444 startoffset = offset - realoffset
1432 startoffset = offset - realoffset
1445 if len(d) - startoffset < length:
1433 if len(d) - startoffset < length:
1446 raise error.RevlogError(
1434 raise error.RevlogError(
1447 _('partial read of revlog %s; expected %d bytes from '
1435 _('partial read of revlog %s; expected %d bytes from '
1448 'offset %d, got %d') %
1436 'offset %d, got %d') %
1449 (self.indexfile if self._inline else self.datafile,
1437 (self.indexfile if self._inline else self.datafile,
1450 length, realoffset, len(d) - startoffset))
1438 length, realoffset, len(d) - startoffset))
1451
1439
1452 return util.buffer(d, startoffset, length)
1440 return util.buffer(d, startoffset, length)
1453
1441
1454 if len(d) < length:
1442 if len(d) < length:
1455 raise error.RevlogError(
1443 raise error.RevlogError(
1456 _('partial read of revlog %s; expected %d bytes from offset '
1444 _('partial read of revlog %s; expected %d bytes from offset '
1457 '%d, got %d') %
1445 '%d, got %d') %
1458 (self.indexfile if self._inline else self.datafile,
1446 (self.indexfile if self._inline else self.datafile,
1459 length, offset, len(d)))
1447 length, offset, len(d)))
1460
1448
1461 return d
1449 return d
1462
1450
1463 def _getsegment(self, offset, length, df=None):
1451 def _getsegment(self, offset, length, df=None):
1464 """Obtain a segment of raw data from the revlog.
1452 """Obtain a segment of raw data from the revlog.
1465
1453
1466 Accepts an absolute offset, length of bytes to obtain, and an
1454 Accepts an absolute offset, length of bytes to obtain, and an
1467 optional file handle to the already-opened revlog. If the file
1455 optional file handle to the already-opened revlog. If the file
1468 handle is used, it's original seek position will not be preserved.
1456 handle is used, it's original seek position will not be preserved.
1469
1457
1470 Requests for data may be returned from a cache.
1458 Requests for data may be returned from a cache.
1471
1459
1472 Returns a str or a buffer instance of raw byte data.
1460 Returns a str or a buffer instance of raw byte data.
1473 """
1461 """
1474 o, d = self._chunkcache
1462 o, d = self._chunkcache
1475 l = len(d)
1463 l = len(d)
1476
1464
1477 # is it in the cache?
1465 # is it in the cache?
1478 cachestart = offset - o
1466 cachestart = offset - o
1479 cacheend = cachestart + length
1467 cacheend = cachestart + length
1480 if cachestart >= 0 and cacheend <= l:
1468 if cachestart >= 0 and cacheend <= l:
1481 if cachestart == 0 and cacheend == l:
1469 if cachestart == 0 and cacheend == l:
1482 return d # avoid a copy
1470 return d # avoid a copy
1483 return util.buffer(d, cachestart, cacheend - cachestart)
1471 return util.buffer(d, cachestart, cacheend - cachestart)
1484
1472
1485 return self._readsegment(offset, length, df=df)
1473 return self._readsegment(offset, length, df=df)
1486
1474
1487 def _getsegmentforrevs(self, startrev, endrev, df=None):
1475 def _getsegmentforrevs(self, startrev, endrev, df=None):
1488 """Obtain a segment of raw data corresponding to a range of revisions.
1476 """Obtain a segment of raw data corresponding to a range of revisions.
1489
1477
1490 Accepts the start and end revisions and an optional already-open
1478 Accepts the start and end revisions and an optional already-open
1491 file handle to be used for reading. If the file handle is read, its
1479 file handle to be used for reading. If the file handle is read, its
1492 seek position will not be preserved.
1480 seek position will not be preserved.
1493
1481
1494 Requests for data may be satisfied by a cache.
1482 Requests for data may be satisfied by a cache.
1495
1483
1496 Returns a 2-tuple of (offset, data) for the requested range of
1484 Returns a 2-tuple of (offset, data) for the requested range of
1497 revisions. Offset is the integer offset from the beginning of the
1485 revisions. Offset is the integer offset from the beginning of the
1498 revlog and data is a str or buffer of the raw byte data.
1486 revlog and data is a str or buffer of the raw byte data.
1499
1487
1500 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1488 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1501 to determine where each revision's data begins and ends.
1489 to determine where each revision's data begins and ends.
1502 """
1490 """
1503 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1491 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1504 # (functions are expensive).
1492 # (functions are expensive).
1505 index = self.index
1493 index = self.index
1506 istart = index[startrev]
1494 istart = index[startrev]
1507 start = int(istart[0] >> 16)
1495 start = int(istart[0] >> 16)
1508 if startrev == endrev:
1496 if startrev == endrev:
1509 end = start + istart[1]
1497 end = start + istart[1]
1510 else:
1498 else:
1511 iend = index[endrev]
1499 iend = index[endrev]
1512 end = int(iend[0] >> 16) + iend[1]
1500 end = int(iend[0] >> 16) + iend[1]
1513
1501
1514 if self._inline:
1502 if self._inline:
1515 start += (startrev + 1) * self._io.size
1503 start += (startrev + 1) * self._io.size
1516 end += (endrev + 1) * self._io.size
1504 end += (endrev + 1) * self._io.size
1517 length = end - start
1505 length = end - start
1518
1506
1519 return start, self._getsegment(start, length, df=df)
1507 return start, self._getsegment(start, length, df=df)
1520
1508
1521 def _chunk(self, rev, df=None):
1509 def _chunk(self, rev, df=None):
1522 """Obtain a single decompressed chunk for a revision.
1510 """Obtain a single decompressed chunk for a revision.
1523
1511
1524 Accepts an integer revision and an optional already-open file handle
1512 Accepts an integer revision and an optional already-open file handle
1525 to be used for reading. If used, the seek position of the file will not
1513 to be used for reading. If used, the seek position of the file will not
1526 be preserved.
1514 be preserved.
1527
1515
1528 Returns a str holding uncompressed data for the requested revision.
1516 Returns a str holding uncompressed data for the requested revision.
1529 """
1517 """
1530 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1518 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1531
1519
1532 def _chunks(self, revs, df=None, targetsize=None):
1520 def _chunks(self, revs, df=None, targetsize=None):
1533 """Obtain decompressed chunks for the specified revisions.
1521 """Obtain decompressed chunks for the specified revisions.
1534
1522
1535 Accepts an iterable of numeric revisions that are assumed to be in
1523 Accepts an iterable of numeric revisions that are assumed to be in
1536 ascending order. Also accepts an optional already-open file handle
1524 ascending order. Also accepts an optional already-open file handle
1537 to be used for reading. If used, the seek position of the file will
1525 to be used for reading. If used, the seek position of the file will
1538 not be preserved.
1526 not be preserved.
1539
1527
1540 This function is similar to calling ``self._chunk()`` multiple times,
1528 This function is similar to calling ``self._chunk()`` multiple times,
1541 but is faster.
1529 but is faster.
1542
1530
1543 Returns a list with decompressed data for each requested revision.
1531 Returns a list with decompressed data for each requested revision.
1544 """
1532 """
1545 if not revs:
1533 if not revs:
1546 return []
1534 return []
1547 start = self.start
1535 start = self.start
1548 length = self.length
1536 length = self.length
1549 inline = self._inline
1537 inline = self._inline
1550 iosize = self._io.size
1538 iosize = self._io.size
1551 buffer = util.buffer
1539 buffer = util.buffer
1552
1540
1553 l = []
1541 l = []
1554 ladd = l.append
1542 ladd = l.append
1555
1543
1556 if not self._withsparseread:
1544 if not self._withsparseread:
1557 slicedchunks = (revs,)
1545 slicedchunks = (revs,)
1558 else:
1546 else:
1559 slicedchunks = deltautil.slicechunk(self, revs,
1547 slicedchunks = deltautil.slicechunk(self, revs,
1560 targetsize=targetsize)
1548 targetsize=targetsize)
1561
1549
1562 for revschunk in slicedchunks:
1550 for revschunk in slicedchunks:
1563 firstrev = revschunk[0]
1551 firstrev = revschunk[0]
1564 # Skip trailing revisions with empty diff
1552 # Skip trailing revisions with empty diff
1565 for lastrev in revschunk[::-1]:
1553 for lastrev in revschunk[::-1]:
1566 if length(lastrev) != 0:
1554 if length(lastrev) != 0:
1567 break
1555 break
1568
1556
1569 try:
1557 try:
1570 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
1558 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
1571 except OverflowError:
1559 except OverflowError:
1572 # issue4215 - we can't cache a run of chunks greater than
1560 # issue4215 - we can't cache a run of chunks greater than
1573 # 2G on Windows
1561 # 2G on Windows
1574 return [self._chunk(rev, df=df) for rev in revschunk]
1562 return [self._chunk(rev, df=df) for rev in revschunk]
1575
1563
1576 decomp = self.decompress
1564 decomp = self.decompress
1577 for rev in revschunk:
1565 for rev in revschunk:
1578 chunkstart = start(rev)
1566 chunkstart = start(rev)
1579 if inline:
1567 if inline:
1580 chunkstart += (rev + 1) * iosize
1568 chunkstart += (rev + 1) * iosize
1581 chunklength = length(rev)
1569 chunklength = length(rev)
1582 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1570 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1583
1571
1584 return l
1572 return l
1585
1573
1586 def _chunkclear(self):
1574 def _chunkclear(self):
1587 """Clear the raw chunk cache."""
1575 """Clear the raw chunk cache."""
1588 self._chunkcache = (0, '')
1576 self._chunkcache = (0, '')
1589
1577
1590 def deltaparent(self, rev):
1578 def deltaparent(self, rev):
1591 """return deltaparent of the given revision"""
1579 """return deltaparent of the given revision"""
1592 base = self.index[rev][3]
1580 base = self.index[rev][3]
1593 if base == rev:
1581 if base == rev:
1594 return nullrev
1582 return nullrev
1595 elif self._generaldelta:
1583 elif self._generaldelta:
1596 return base
1584 return base
1597 else:
1585 else:
1598 return rev - 1
1586 return rev - 1
1599
1587
1600 def issnapshot(self, rev):
1588 def issnapshot(self, rev):
1601 """tells whether rev is a snapshot
1589 """tells whether rev is a snapshot
1602 """
1590 """
1603 if not self._sparserevlog:
1591 if not self._sparserevlog:
1604 return self.deltaparent(rev) == nullrev
1592 return self.deltaparent(rev) == nullrev
1605 elif util.safehasattr(self.index, 'issnapshot'):
1593 elif util.safehasattr(self.index, 'issnapshot'):
1606 # directly assign the method to cache the testing and access
1594 # directly assign the method to cache the testing and access
1607 self.issnapshot = self.index.issnapshot
1595 self.issnapshot = self.index.issnapshot
1608 return self.issnapshot(rev)
1596 return self.issnapshot(rev)
1609 if rev == nullrev:
1597 if rev == nullrev:
1610 return True
1598 return True
1611 entry = self.index[rev]
1599 entry = self.index[rev]
1612 base = entry[3]
1600 base = entry[3]
1613 if base == rev:
1601 if base == rev:
1614 return True
1602 return True
1615 if base == nullrev:
1603 if base == nullrev:
1616 return True
1604 return True
1617 p1 = entry[5]
1605 p1 = entry[5]
1618 p2 = entry[6]
1606 p2 = entry[6]
1619 if base == p1 or base == p2:
1607 if base == p1 or base == p2:
1620 return False
1608 return False
1621 return self.issnapshot(base)
1609 return self.issnapshot(base)
1622
1610
1623 def snapshotdepth(self, rev):
1611 def snapshotdepth(self, rev):
1624 """number of snapshot in the chain before this one"""
1612 """number of snapshot in the chain before this one"""
1625 if not self.issnapshot(rev):
1613 if not self.issnapshot(rev):
1626 raise error.ProgrammingError('revision %d not a snapshot')
1614 raise error.ProgrammingError('revision %d not a snapshot')
1627 return len(self._deltachain(rev)[0]) - 1
1615 return len(self._deltachain(rev)[0]) - 1
1628
1616
1629 def revdiff(self, rev1, rev2):
1617 def revdiff(self, rev1, rev2):
1630 """return or calculate a delta between two revisions
1618 """return or calculate a delta between two revisions
1631
1619
1632 The delta calculated is in binary form and is intended to be written to
1620 The delta calculated is in binary form and is intended to be written to
1633 revlog data directly. So this function needs raw revision data.
1621 revlog data directly. So this function needs raw revision data.
1634 """
1622 """
1635 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1623 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1636 return bytes(self._chunk(rev2))
1624 return bytes(self._chunk(rev2))
1637
1625
1638 return mdiff.textdiff(self.revision(rev1, raw=True),
1626 return mdiff.textdiff(self.revision(rev1, raw=True),
1639 self.revision(rev2, raw=True))
1627 self.revision(rev2, raw=True))
1640
1628
1641 def revision(self, nodeorrev, _df=None, raw=False):
1629 def revision(self, nodeorrev, _df=None, raw=False):
1642 """return an uncompressed revision of a given node or revision
1630 """return an uncompressed revision of a given node or revision
1643 number.
1631 number.
1644
1632
1645 _df - an existing file handle to read from. (internal-only)
1633 _df - an existing file handle to read from. (internal-only)
1646 raw - an optional argument specifying if the revision data is to be
1634 raw - an optional argument specifying if the revision data is to be
1647 treated as raw data when applying flag transforms. 'raw' should be set
1635 treated as raw data when applying flag transforms. 'raw' should be set
1648 to True when generating changegroups or in debug commands.
1636 to True when generating changegroups or in debug commands.
1649 """
1637 """
1650 return self._revisiondata(nodeorrev, _df, raw=raw)
1638 return self._revisiondata(nodeorrev, _df, raw=raw)
1651
1639
1652 def _revisiondata(self, nodeorrev, _df=None, raw=False):
1640 def _revisiondata(self, nodeorrev, _df=None, raw=False):
1653 if isinstance(nodeorrev, int):
1641 if isinstance(nodeorrev, int):
1654 rev = nodeorrev
1642 rev = nodeorrev
1655 node = self.node(rev)
1643 node = self.node(rev)
1656 else:
1644 else:
1657 node = nodeorrev
1645 node = nodeorrev
1658 rev = None
1646 rev = None
1659
1647
1660 cachedrev = None
1648 cachedrev = None
1661 flags = None
1649 flags = None
1662 rawtext = None
1650 rawtext = None
1663 if node == nullid:
1651 if node == nullid:
1664 return ""
1652 return ""
1665 if self._revisioncache:
1653 if self._revisioncache:
1666 if self._revisioncache[0] == node:
1654 if self._revisioncache[0] == node:
1667 # _cache only stores rawtext
1655 # _cache only stores rawtext
1668 if raw:
1656 if raw:
1669 return self._revisioncache[2]
1657 return self._revisioncache[2]
1670 # duplicated, but good for perf
1658 # duplicated, but good for perf
1671 if rev is None:
1659 if rev is None:
1672 rev = self.rev(node)
1660 rev = self.rev(node)
1673 if flags is None:
1661 if flags is None:
1674 flags = self.flags(rev)
1662 flags = self.flags(rev)
1675 # no extra flags set, no flag processor runs, text = rawtext
1663 # no extra flags set, no flag processor runs, text = rawtext
1676 if flags == REVIDX_DEFAULT_FLAGS:
1664 if flags == REVIDX_DEFAULT_FLAGS:
1677 return self._revisioncache[2]
1665 return self._revisioncache[2]
1678 # rawtext is reusable. need to run flag processor
1666 # rawtext is reusable. need to run flag processor
1679 rawtext = self._revisioncache[2]
1667 rawtext = self._revisioncache[2]
1680
1668
1681 cachedrev = self._revisioncache[1]
1669 cachedrev = self._revisioncache[1]
1682
1670
1683 # look up what we need to read
1671 # look up what we need to read
1684 if rawtext is None:
1672 if rawtext is None:
1685 if rev is None:
1673 if rev is None:
1686 rev = self.rev(node)
1674 rev = self.rev(node)
1687
1675
1688 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1676 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1689 if stopped:
1677 if stopped:
1690 rawtext = self._revisioncache[2]
1678 rawtext = self._revisioncache[2]
1691
1679
1692 # drop cache to save memory
1680 # drop cache to save memory
1693 self._revisioncache = None
1681 self._revisioncache = None
1694
1682
1695 targetsize = None
1683 targetsize = None
1696 rawsize = self.index[rev][2]
1684 rawsize = self.index[rev][2]
1697 if 0 <= rawsize:
1685 if 0 <= rawsize:
1698 targetsize = 4 * rawsize
1686 targetsize = 4 * rawsize
1699
1687
1700 bins = self._chunks(chain, df=_df, targetsize=targetsize)
1688 bins = self._chunks(chain, df=_df, targetsize=targetsize)
1701 if rawtext is None:
1689 if rawtext is None:
1702 rawtext = bytes(bins[0])
1690 rawtext = bytes(bins[0])
1703 bins = bins[1:]
1691 bins = bins[1:]
1704
1692
1705 rawtext = mdiff.patches(rawtext, bins)
1693 rawtext = mdiff.patches(rawtext, bins)
1706 self._revisioncache = (node, rev, rawtext)
1694 self._revisioncache = (node, rev, rawtext)
1707
1695
1708 if flags is None:
1696 if flags is None:
1709 if rev is None:
1697 if rev is None:
1710 rev = self.rev(node)
1698 rev = self.rev(node)
1711 flags = self.flags(rev)
1699 flags = self.flags(rev)
1712
1700
1713 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1701 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1714 if validatehash:
1702 if validatehash:
1715 self.checkhash(text, node, rev=rev)
1703 self.checkhash(text, node, rev=rev)
1716
1704
1717 return text
1705 return text
1718
1706
1719 def rawdata(self, nodeorrev, _df=None, raw=False):
1707 def rawdata(self, nodeorrev, _df=None, raw=False):
1720 """return an uncompressed raw data of a given node or revision number.
1708 """return an uncompressed raw data of a given node or revision number.
1721
1709
1722 _df - an existing file handle to read from. (internal-only)
1710 _df - an existing file handle to read from. (internal-only)
1723 """
1711 """
1724 return self._revisiondata(nodeorrev, _df, raw=True)
1712 return self._revisiondata(nodeorrev, _df, raw=True)
1725
1713
1726 def hash(self, text, p1, p2):
1714 def hash(self, text, p1, p2):
1727 """Compute a node hash.
1715 """Compute a node hash.
1728
1716
1729 Available as a function so that subclasses can replace the hash
1717 Available as a function so that subclasses can replace the hash
1730 as needed.
1718 as needed.
1731 """
1719 """
1732 return storageutil.hashrevisionsha1(text, p1, p2)
1720 return storageutil.hashrevisionsha1(text, p1, p2)
1733
1721
1734 def _processflags(self, text, flags, operation, raw=False):
1722 def _processflags(self, text, flags, operation, raw=False):
1735 """Inspect revision data flags and applies transforms defined by
1723 """Inspect revision data flags and applies transforms defined by
1736 registered flag processors.
1724 registered flag processors.
1737
1725
1738 ``text`` - the revision data to process
1726 ``text`` - the revision data to process
1739 ``flags`` - the revision flags
1727 ``flags`` - the revision flags
1740 ``operation`` - the operation being performed (read or write)
1728 ``operation`` - the operation being performed (read or write)
1741 ``raw`` - an optional argument describing if the raw transform should be
1729 ``raw`` - an optional argument describing if the raw transform should be
1742 applied.
1730 applied.
1743
1731
1744 This method processes the flags in the order (or reverse order if
1732 This method processes the flags in the order (or reverse order if
1745 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1733 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1746 flag processors registered for present flags. The order of flags defined
1734 flag processors registered for present flags. The order of flags defined
1747 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1735 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1748
1736
1749 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1737 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1750 processed text and ``validatehash`` is a bool indicating whether the
1738 processed text and ``validatehash`` is a bool indicating whether the
1751 returned text should be checked for hash integrity.
1739 returned text should be checked for hash integrity.
1752
1740
1753 Note: If the ``raw`` argument is set, it has precedence over the
1741 Note: If the ``raw`` argument is set, it has precedence over the
1754 operation and will only update the value of ``validatehash``.
1742 operation and will only update the value of ``validatehash``.
1755 """
1743 """
1756 # fast path: no flag processors will run
1744 # fast path: no flag processors will run
1757 if flags == 0:
1745 if flags == 0:
1758 return text, True
1746 return text, True
1759 if not operation in ('read', 'write'):
1747 if not operation in ('read', 'write'):
1760 raise error.ProgrammingError(_("invalid '%s' operation") %
1748 raise error.ProgrammingError(_("invalid '%s' operation") %
1761 operation)
1749 operation)
1762 # Check all flags are known.
1750 # Check all flags are known.
1763 if flags & ~flagutil.REVIDX_KNOWN_FLAGS:
1751 if flags & ~flagutil.REVIDX_KNOWN_FLAGS:
1764 raise error.RevlogError(_("incompatible revision flag '%#x'") %
1752 raise error.RevlogError(_("incompatible revision flag '%#x'") %
1765 (flags & ~flagutil.REVIDX_KNOWN_FLAGS))
1753 (flags & ~flagutil.REVIDX_KNOWN_FLAGS))
1766 validatehash = True
1754 validatehash = True
1767 # Depending on the operation (read or write), the order might be
1755 # Depending on the operation (read or write), the order might be
1768 # reversed due to non-commutative transforms.
1756 # reversed due to non-commutative transforms.
1769 orderedflags = REVIDX_FLAGS_ORDER
1757 orderedflags = REVIDX_FLAGS_ORDER
1770 if operation == 'write':
1758 if operation == 'write':
1771 orderedflags = reversed(orderedflags)
1759 orderedflags = reversed(orderedflags)
1772
1760
1773 for flag in orderedflags:
1761 for flag in orderedflags:
1774 # If a flagprocessor has been registered for a known flag, apply the
1762 # If a flagprocessor has been registered for a known flag, apply the
1775 # related operation transform and update result tuple.
1763 # related operation transform and update result tuple.
1776 if flag & flags:
1764 if flag & flags:
1777 vhash = True
1765 vhash = True
1778
1766
1779 if flag not in self._flagprocessors:
1767 if flag not in self._flagprocessors:
1780 message = _("missing processor for flag '%#x'") % (flag)
1768 message = _("missing processor for flag '%#x'") % (flag)
1781 raise error.RevlogError(message)
1769 raise error.RevlogError(message)
1782
1770
1783 processor = self._flagprocessors[flag]
1771 processor = self._flagprocessors[flag]
1784 if processor is not None:
1772 if processor is not None:
1785 readtransform, writetransform, rawtransform = processor
1773 readtransform, writetransform, rawtransform = processor
1786
1774
1787 if raw:
1775 if raw:
1788 vhash = rawtransform(self, text)
1776 vhash = rawtransform(self, text)
1789 elif operation == 'read':
1777 elif operation == 'read':
1790 text, vhash = readtransform(self, text)
1778 text, vhash = readtransform(self, text)
1791 else: # write operation
1779 else: # write operation
1792 text, vhash = writetransform(self, text)
1780 text, vhash = writetransform(self, text)
1793 validatehash = validatehash and vhash
1781 validatehash = validatehash and vhash
1794
1782
1795 return text, validatehash
1783 return text, validatehash
1796
1784
1797 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1785 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1798 """Check node hash integrity.
1786 """Check node hash integrity.
1799
1787
1800 Available as a function so that subclasses can extend hash mismatch
1788 Available as a function so that subclasses can extend hash mismatch
1801 behaviors as needed.
1789 behaviors as needed.
1802 """
1790 """
1803 try:
1791 try:
1804 if p1 is None and p2 is None:
1792 if p1 is None and p2 is None:
1805 p1, p2 = self.parents(node)
1793 p1, p2 = self.parents(node)
1806 if node != self.hash(text, p1, p2):
1794 if node != self.hash(text, p1, p2):
1807 # Clear the revision cache on hash failure. The revision cache
1795 # Clear the revision cache on hash failure. The revision cache
1808 # only stores the raw revision and clearing the cache does have
1796 # only stores the raw revision and clearing the cache does have
1809 # the side-effect that we won't have a cache hit when the raw
1797 # the side-effect that we won't have a cache hit when the raw
1810 # revision data is accessed. But this case should be rare and
1798 # revision data is accessed. But this case should be rare and
1811 # it is extra work to teach the cache about the hash
1799 # it is extra work to teach the cache about the hash
1812 # verification state.
1800 # verification state.
1813 if self._revisioncache and self._revisioncache[0] == node:
1801 if self._revisioncache and self._revisioncache[0] == node:
1814 self._revisioncache = None
1802 self._revisioncache = None
1815
1803
1816 revornode = rev
1804 revornode = rev
1817 if revornode is None:
1805 if revornode is None:
1818 revornode = templatefilters.short(hex(node))
1806 revornode = templatefilters.short(hex(node))
1819 raise error.RevlogError(_("integrity check failed on %s:%s")
1807 raise error.RevlogError(_("integrity check failed on %s:%s")
1820 % (self.indexfile, pycompat.bytestr(revornode)))
1808 % (self.indexfile, pycompat.bytestr(revornode)))
1821 except error.RevlogError:
1809 except error.RevlogError:
1822 if self._censorable and storageutil.iscensoredtext(text):
1810 if self._censorable and storageutil.iscensoredtext(text):
1823 raise error.CensoredNodeError(self.indexfile, node, text)
1811 raise error.CensoredNodeError(self.indexfile, node, text)
1824 raise
1812 raise
1825
1813
1826 def _enforceinlinesize(self, tr, fp=None):
1814 def _enforceinlinesize(self, tr, fp=None):
1827 """Check if the revlog is too big for inline and convert if so.
1815 """Check if the revlog is too big for inline and convert if so.
1828
1816
1829 This should be called after revisions are added to the revlog. If the
1817 This should be called after revisions are added to the revlog. If the
1830 revlog has grown too large to be an inline revlog, it will convert it
1818 revlog has grown too large to be an inline revlog, it will convert it
1831 to use multiple index and data files.
1819 to use multiple index and data files.
1832 """
1820 """
1833 tiprev = len(self) - 1
1821 tiprev = len(self) - 1
1834 if (not self._inline or
1822 if (not self._inline or
1835 (self.start(tiprev) + self.length(tiprev)) < _maxinline):
1823 (self.start(tiprev) + self.length(tiprev)) < _maxinline):
1836 return
1824 return
1837
1825
1838 trinfo = tr.find(self.indexfile)
1826 trinfo = tr.find(self.indexfile)
1839 if trinfo is None:
1827 if trinfo is None:
1840 raise error.RevlogError(_("%s not found in the transaction")
1828 raise error.RevlogError(_("%s not found in the transaction")
1841 % self.indexfile)
1829 % self.indexfile)
1842
1830
1843 trindex = trinfo[2]
1831 trindex = trinfo[2]
1844 if trindex is not None:
1832 if trindex is not None:
1845 dataoff = self.start(trindex)
1833 dataoff = self.start(trindex)
1846 else:
1834 else:
1847 # revlog was stripped at start of transaction, use all leftover data
1835 # revlog was stripped at start of transaction, use all leftover data
1848 trindex = len(self) - 1
1836 trindex = len(self) - 1
1849 dataoff = self.end(tiprev)
1837 dataoff = self.end(tiprev)
1850
1838
1851 tr.add(self.datafile, dataoff)
1839 tr.add(self.datafile, dataoff)
1852
1840
1853 if fp:
1841 if fp:
1854 fp.flush()
1842 fp.flush()
1855 fp.close()
1843 fp.close()
1856 # We can't use the cached file handle after close(). So prevent
1844 # We can't use the cached file handle after close(). So prevent
1857 # its usage.
1845 # its usage.
1858 self._writinghandles = None
1846 self._writinghandles = None
1859
1847
1860 with self._indexfp('r') as ifh, self._datafp('w') as dfh:
1848 with self._indexfp('r') as ifh, self._datafp('w') as dfh:
1861 for r in self:
1849 for r in self:
1862 dfh.write(self._getsegmentforrevs(r, r, df=ifh)[1])
1850 dfh.write(self._getsegmentforrevs(r, r, df=ifh)[1])
1863
1851
1864 with self._indexfp('w') as fp:
1852 with self._indexfp('w') as fp:
1865 self.version &= ~FLAG_INLINE_DATA
1853 self.version &= ~FLAG_INLINE_DATA
1866 self._inline = False
1854 self._inline = False
1867 io = self._io
1855 io = self._io
1868 for i in self:
1856 for i in self:
1869 e = io.packentry(self.index[i], self.node, self.version, i)
1857 e = io.packentry(self.index[i], self.node, self.version, i)
1870 fp.write(e)
1858 fp.write(e)
1871
1859
1872 # the temp file replace the real index when we exit the context
1860 # the temp file replace the real index when we exit the context
1873 # manager
1861 # manager
1874
1862
1875 tr.replace(self.indexfile, trindex * self._io.size)
1863 tr.replace(self.indexfile, trindex * self._io.size)
1876 self._chunkclear()
1864 self._chunkclear()
1877
1865
1878 def _nodeduplicatecallback(self, transaction, node):
1866 def _nodeduplicatecallback(self, transaction, node):
1879 """called when trying to add a node already stored.
1867 """called when trying to add a node already stored.
1880 """
1868 """
1881
1869
1882 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1870 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1883 node=None, flags=REVIDX_DEFAULT_FLAGS, deltacomputer=None):
1871 node=None, flags=REVIDX_DEFAULT_FLAGS, deltacomputer=None):
1884 """add a revision to the log
1872 """add a revision to the log
1885
1873
1886 text - the revision data to add
1874 text - the revision data to add
1887 transaction - the transaction object used for rollback
1875 transaction - the transaction object used for rollback
1888 link - the linkrev data to add
1876 link - the linkrev data to add
1889 p1, p2 - the parent nodeids of the revision
1877 p1, p2 - the parent nodeids of the revision
1890 cachedelta - an optional precomputed delta
1878 cachedelta - an optional precomputed delta
1891 node - nodeid of revision; typically node is not specified, and it is
1879 node - nodeid of revision; typically node is not specified, and it is
1892 computed by default as hash(text, p1, p2), however subclasses might
1880 computed by default as hash(text, p1, p2), however subclasses might
1893 use different hashing method (and override checkhash() in such case)
1881 use different hashing method (and override checkhash() in such case)
1894 flags - the known flags to set on the revision
1882 flags - the known flags to set on the revision
1895 deltacomputer - an optional deltacomputer instance shared between
1883 deltacomputer - an optional deltacomputer instance shared between
1896 multiple calls
1884 multiple calls
1897 """
1885 """
1898 if link == nullrev:
1886 if link == nullrev:
1899 raise error.RevlogError(_("attempted to add linkrev -1 to %s")
1887 raise error.RevlogError(_("attempted to add linkrev -1 to %s")
1900 % self.indexfile)
1888 % self.indexfile)
1901
1889
1902 if flags:
1890 if flags:
1903 node = node or self.hash(text, p1, p2)
1891 node = node or self.hash(text, p1, p2)
1904
1892
1905 rawtext, validatehash = self._processflags(text, flags, 'write')
1893 rawtext, validatehash = self._processflags(text, flags, 'write')
1906
1894
1907 # If the flag processor modifies the revision data, ignore any provided
1895 # If the flag processor modifies the revision data, ignore any provided
1908 # cachedelta.
1896 # cachedelta.
1909 if rawtext != text:
1897 if rawtext != text:
1910 cachedelta = None
1898 cachedelta = None
1911
1899
1912 if len(rawtext) > _maxentrysize:
1900 if len(rawtext) > _maxentrysize:
1913 raise error.RevlogError(
1901 raise error.RevlogError(
1914 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1902 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1915 % (self.indexfile, len(rawtext)))
1903 % (self.indexfile, len(rawtext)))
1916
1904
1917 node = node or self.hash(rawtext, p1, p2)
1905 node = node or self.hash(rawtext, p1, p2)
1918 if node in self.nodemap:
1906 if node in self.nodemap:
1919 return node
1907 return node
1920
1908
1921 if validatehash:
1909 if validatehash:
1922 self.checkhash(rawtext, node, p1=p1, p2=p2)
1910 self.checkhash(rawtext, node, p1=p1, p2=p2)
1923
1911
1924 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
1912 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
1925 flags, cachedelta=cachedelta,
1913 flags, cachedelta=cachedelta,
1926 deltacomputer=deltacomputer)
1914 deltacomputer=deltacomputer)
1927
1915
1928 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
1916 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
1929 cachedelta=None, deltacomputer=None):
1917 cachedelta=None, deltacomputer=None):
1930 """add a raw revision with known flags, node and parents
1918 """add a raw revision with known flags, node and parents
1931 useful when reusing a revision not stored in this revlog (ex: received
1919 useful when reusing a revision not stored in this revlog (ex: received
1932 over wire, or read from an external bundle).
1920 over wire, or read from an external bundle).
1933 """
1921 """
1934 dfh = None
1922 dfh = None
1935 if not self._inline:
1923 if not self._inline:
1936 dfh = self._datafp("a+")
1924 dfh = self._datafp("a+")
1937 ifh = self._indexfp("a+")
1925 ifh = self._indexfp("a+")
1938 try:
1926 try:
1939 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1927 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1940 flags, cachedelta, ifh, dfh,
1928 flags, cachedelta, ifh, dfh,
1941 deltacomputer=deltacomputer)
1929 deltacomputer=deltacomputer)
1942 finally:
1930 finally:
1943 if dfh:
1931 if dfh:
1944 dfh.close()
1932 dfh.close()
1945 ifh.close()
1933 ifh.close()
1946
1934
1947 def compress(self, data):
1935 def compress(self, data):
1948 """Generate a possibly-compressed representation of data."""
1936 """Generate a possibly-compressed representation of data."""
1949 if not data:
1937 if not data:
1950 return '', data
1938 return '', data
1951
1939
1952 compressed = self._compressor.compress(data)
1940 compressed = self._compressor.compress(data)
1953
1941
1954 if compressed:
1942 if compressed:
1955 # The revlog compressor added the header in the returned data.
1943 # The revlog compressor added the header in the returned data.
1956 return '', compressed
1944 return '', compressed
1957
1945
1958 if data[0:1] == '\0':
1946 if data[0:1] == '\0':
1959 return '', data
1947 return '', data
1960 return 'u', data
1948 return 'u', data
1961
1949
1962 def decompress(self, data):
1950 def decompress(self, data):
1963 """Decompress a revlog chunk.
1951 """Decompress a revlog chunk.
1964
1952
1965 The chunk is expected to begin with a header identifying the
1953 The chunk is expected to begin with a header identifying the
1966 format type so it can be routed to an appropriate decompressor.
1954 format type so it can be routed to an appropriate decompressor.
1967 """
1955 """
1968 if not data:
1956 if not data:
1969 return data
1957 return data
1970
1958
1971 # Revlogs are read much more frequently than they are written and many
1959 # Revlogs are read much more frequently than they are written and many
1972 # chunks only take microseconds to decompress, so performance is
1960 # chunks only take microseconds to decompress, so performance is
1973 # important here.
1961 # important here.
1974 #
1962 #
1975 # We can make a few assumptions about revlogs:
1963 # We can make a few assumptions about revlogs:
1976 #
1964 #
1977 # 1) the majority of chunks will be compressed (as opposed to inline
1965 # 1) the majority of chunks will be compressed (as opposed to inline
1978 # raw data).
1966 # raw data).
1979 # 2) decompressing *any* data will likely by at least 10x slower than
1967 # 2) decompressing *any* data will likely by at least 10x slower than
1980 # returning raw inline data.
1968 # returning raw inline data.
1981 # 3) we want to prioritize common and officially supported compression
1969 # 3) we want to prioritize common and officially supported compression
1982 # engines
1970 # engines
1983 #
1971 #
1984 # It follows that we want to optimize for "decompress compressed data
1972 # It follows that we want to optimize for "decompress compressed data
1985 # when encoded with common and officially supported compression engines"
1973 # when encoded with common and officially supported compression engines"
1986 # case over "raw data" and "data encoded by less common or non-official
1974 # case over "raw data" and "data encoded by less common or non-official
1987 # compression engines." That is why we have the inline lookup first
1975 # compression engines." That is why we have the inline lookup first
1988 # followed by the compengines lookup.
1976 # followed by the compengines lookup.
1989 #
1977 #
1990 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1978 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1991 # compressed chunks. And this matters for changelog and manifest reads.
1979 # compressed chunks. And this matters for changelog and manifest reads.
1992 t = data[0:1]
1980 t = data[0:1]
1993
1981
1994 if t == 'x':
1982 if t == 'x':
1995 try:
1983 try:
1996 return _zlibdecompress(data)
1984 return _zlibdecompress(data)
1997 except zlib.error as e:
1985 except zlib.error as e:
1998 raise error.RevlogError(_('revlog decompress error: %s') %
1986 raise error.RevlogError(_('revlog decompress error: %s') %
1999 stringutil.forcebytestr(e))
1987 stringutil.forcebytestr(e))
2000 # '\0' is more common than 'u' so it goes first.
1988 # '\0' is more common than 'u' so it goes first.
2001 elif t == '\0':
1989 elif t == '\0':
2002 return data
1990 return data
2003 elif t == 'u':
1991 elif t == 'u':
2004 return util.buffer(data, 1)
1992 return util.buffer(data, 1)
2005
1993
2006 try:
1994 try:
2007 compressor = self._decompressors[t]
1995 compressor = self._decompressors[t]
2008 except KeyError:
1996 except KeyError:
2009 try:
1997 try:
2010 engine = util.compengines.forrevlogheader(t)
1998 engine = util.compengines.forrevlogheader(t)
2011 compressor = engine.revlogcompressor(self._compengineopts)
1999 compressor = engine.revlogcompressor(self._compengineopts)
2012 self._decompressors[t] = compressor
2000 self._decompressors[t] = compressor
2013 except KeyError:
2001 except KeyError:
2014 raise error.RevlogError(_('unknown compression type %r') % t)
2002 raise error.RevlogError(_('unknown compression type %r') % t)
2015
2003
2016 return compressor.decompress(data)
2004 return compressor.decompress(data)
2017
2005
2018 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
2006 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
2019 cachedelta, ifh, dfh, alwayscache=False,
2007 cachedelta, ifh, dfh, alwayscache=False,
2020 deltacomputer=None):
2008 deltacomputer=None):
2021 """internal function to add revisions to the log
2009 """internal function to add revisions to the log
2022
2010
2023 see addrevision for argument descriptions.
2011 see addrevision for argument descriptions.
2024
2012
2025 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2013 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2026
2014
2027 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2015 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2028 be used.
2016 be used.
2029
2017
2030 invariants:
2018 invariants:
2031 - rawtext is optional (can be None); if not set, cachedelta must be set.
2019 - rawtext is optional (can be None); if not set, cachedelta must be set.
2032 if both are set, they must correspond to each other.
2020 if both are set, they must correspond to each other.
2033 """
2021 """
2034 if node == nullid:
2022 if node == nullid:
2035 raise error.RevlogError(_("%s: attempt to add null revision") %
2023 raise error.RevlogError(_("%s: attempt to add null revision") %
2036 self.indexfile)
2024 self.indexfile)
2037 if node == wdirid or node in wdirfilenodeids:
2025 if node == wdirid or node in wdirfilenodeids:
2038 raise error.RevlogError(_("%s: attempt to add wdir revision") %
2026 raise error.RevlogError(_("%s: attempt to add wdir revision") %
2039 self.indexfile)
2027 self.indexfile)
2040
2028
2041 if self._inline:
2029 if self._inline:
2042 fh = ifh
2030 fh = ifh
2043 else:
2031 else:
2044 fh = dfh
2032 fh = dfh
2045
2033
2046 btext = [rawtext]
2034 btext = [rawtext]
2047
2035
2048 curr = len(self)
2036 curr = len(self)
2049 prev = curr - 1
2037 prev = curr - 1
2050 offset = self.end(prev)
2038 offset = self.end(prev)
2051 p1r, p2r = self.rev(p1), self.rev(p2)
2039 p1r, p2r = self.rev(p1), self.rev(p2)
2052
2040
2053 # full versions are inserted when the needed deltas
2041 # full versions are inserted when the needed deltas
2054 # become comparable to the uncompressed text
2042 # become comparable to the uncompressed text
2055 if rawtext is None:
2043 if rawtext is None:
2056 # need rawtext size, before changed by flag processors, which is
2044 # need rawtext size, before changed by flag processors, which is
2057 # the non-raw size. use revlog explicitly to avoid filelog's extra
2045 # the non-raw size. use revlog explicitly to avoid filelog's extra
2058 # logic that might remove metadata size.
2046 # logic that might remove metadata size.
2059 textlen = mdiff.patchedsize(revlog.size(self, cachedelta[0]),
2047 textlen = mdiff.patchedsize(revlog.size(self, cachedelta[0]),
2060 cachedelta[1])
2048 cachedelta[1])
2061 else:
2049 else:
2062 textlen = len(rawtext)
2050 textlen = len(rawtext)
2063
2051
2064 if deltacomputer is None:
2052 if deltacomputer is None:
2065 deltacomputer = deltautil.deltacomputer(self)
2053 deltacomputer = deltautil.deltacomputer(self)
2066
2054
2067 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2055 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2068
2056
2069 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2057 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2070
2058
2071 e = (offset_type(offset, flags), deltainfo.deltalen, textlen,
2059 e = (offset_type(offset, flags), deltainfo.deltalen, textlen,
2072 deltainfo.base, link, p1r, p2r, node)
2060 deltainfo.base, link, p1r, p2r, node)
2073 self.index.append(e)
2061 self.index.append(e)
2074 self.nodemap[node] = curr
2062 self.nodemap[node] = curr
2075
2063
2076 # Reset the pure node cache start lookup offset to account for new
2064 # Reset the pure node cache start lookup offset to account for new
2077 # revision.
2065 # revision.
2078 if self._nodepos is not None:
2066 if self._nodepos is not None:
2079 self._nodepos = curr
2067 self._nodepos = curr
2080
2068
2081 entry = self._io.packentry(e, self.node, self.version, curr)
2069 entry = self._io.packentry(e, self.node, self.version, curr)
2082 self._writeentry(transaction, ifh, dfh, entry, deltainfo.data,
2070 self._writeentry(transaction, ifh, dfh, entry, deltainfo.data,
2083 link, offset)
2071 link, offset)
2084
2072
2085 rawtext = btext[0]
2073 rawtext = btext[0]
2086
2074
2087 if alwayscache and rawtext is None:
2075 if alwayscache and rawtext is None:
2088 rawtext = deltacomputer.buildtext(revinfo, fh)
2076 rawtext = deltacomputer.buildtext(revinfo, fh)
2089
2077
2090 if type(rawtext) == bytes: # only accept immutable objects
2078 if type(rawtext) == bytes: # only accept immutable objects
2091 self._revisioncache = (node, curr, rawtext)
2079 self._revisioncache = (node, curr, rawtext)
2092 self._chainbasecache[curr] = deltainfo.chainbase
2080 self._chainbasecache[curr] = deltainfo.chainbase
2093 return node
2081 return node
2094
2082
2095 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2083 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2096 # Files opened in a+ mode have inconsistent behavior on various
2084 # Files opened in a+ mode have inconsistent behavior on various
2097 # platforms. Windows requires that a file positioning call be made
2085 # platforms. Windows requires that a file positioning call be made
2098 # when the file handle transitions between reads and writes. See
2086 # when the file handle transitions between reads and writes. See
2099 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2087 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2100 # platforms, Python or the platform itself can be buggy. Some versions
2088 # platforms, Python or the platform itself can be buggy. Some versions
2101 # of Solaris have been observed to not append at the end of the file
2089 # of Solaris have been observed to not append at the end of the file
2102 # if the file was seeked to before the end. See issue4943 for more.
2090 # if the file was seeked to before the end. See issue4943 for more.
2103 #
2091 #
2104 # We work around this issue by inserting a seek() before writing.
2092 # We work around this issue by inserting a seek() before writing.
2105 # Note: This is likely not necessary on Python 3. However, because
2093 # Note: This is likely not necessary on Python 3. However, because
2106 # the file handle is reused for reads and may be seeked there, we need
2094 # the file handle is reused for reads and may be seeked there, we need
2107 # to be careful before changing this.
2095 # to be careful before changing this.
2108 ifh.seek(0, os.SEEK_END)
2096 ifh.seek(0, os.SEEK_END)
2109 if dfh:
2097 if dfh:
2110 dfh.seek(0, os.SEEK_END)
2098 dfh.seek(0, os.SEEK_END)
2111
2099
2112 curr = len(self) - 1
2100 curr = len(self) - 1
2113 if not self._inline:
2101 if not self._inline:
2114 transaction.add(self.datafile, offset)
2102 transaction.add(self.datafile, offset)
2115 transaction.add(self.indexfile, curr * len(entry))
2103 transaction.add(self.indexfile, curr * len(entry))
2116 if data[0]:
2104 if data[0]:
2117 dfh.write(data[0])
2105 dfh.write(data[0])
2118 dfh.write(data[1])
2106 dfh.write(data[1])
2119 ifh.write(entry)
2107 ifh.write(entry)
2120 else:
2108 else:
2121 offset += curr * self._io.size
2109 offset += curr * self._io.size
2122 transaction.add(self.indexfile, offset, curr)
2110 transaction.add(self.indexfile, offset, curr)
2123 ifh.write(entry)
2111 ifh.write(entry)
2124 ifh.write(data[0])
2112 ifh.write(data[0])
2125 ifh.write(data[1])
2113 ifh.write(data[1])
2126 self._enforceinlinesize(transaction, ifh)
2114 self._enforceinlinesize(transaction, ifh)
2127
2115
2128 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2116 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2129 """
2117 """
2130 add a delta group
2118 add a delta group
2131
2119
2132 given a set of deltas, add them to the revision log. the
2120 given a set of deltas, add them to the revision log. the
2133 first delta is against its parent, which should be in our
2121 first delta is against its parent, which should be in our
2134 log, the rest are against the previous delta.
2122 log, the rest are against the previous delta.
2135
2123
2136 If ``addrevisioncb`` is defined, it will be called with arguments of
2124 If ``addrevisioncb`` is defined, it will be called with arguments of
2137 this revlog and the node that was added.
2125 this revlog and the node that was added.
2138 """
2126 """
2139
2127
2140 if self._writinghandles:
2128 if self._writinghandles:
2141 raise error.ProgrammingError('cannot nest addgroup() calls')
2129 raise error.ProgrammingError('cannot nest addgroup() calls')
2142
2130
2143 nodes = []
2131 nodes = []
2144
2132
2145 r = len(self)
2133 r = len(self)
2146 end = 0
2134 end = 0
2147 if r:
2135 if r:
2148 end = self.end(r - 1)
2136 end = self.end(r - 1)
2149 ifh = self._indexfp("a+")
2137 ifh = self._indexfp("a+")
2150 isize = r * self._io.size
2138 isize = r * self._io.size
2151 if self._inline:
2139 if self._inline:
2152 transaction.add(self.indexfile, end + isize, r)
2140 transaction.add(self.indexfile, end + isize, r)
2153 dfh = None
2141 dfh = None
2154 else:
2142 else:
2155 transaction.add(self.indexfile, isize, r)
2143 transaction.add(self.indexfile, isize, r)
2156 transaction.add(self.datafile, end)
2144 transaction.add(self.datafile, end)
2157 dfh = self._datafp("a+")
2145 dfh = self._datafp("a+")
2158 def flush():
2146 def flush():
2159 if dfh:
2147 if dfh:
2160 dfh.flush()
2148 dfh.flush()
2161 ifh.flush()
2149 ifh.flush()
2162
2150
2163 self._writinghandles = (ifh, dfh)
2151 self._writinghandles = (ifh, dfh)
2164
2152
2165 try:
2153 try:
2166 deltacomputer = deltautil.deltacomputer(self)
2154 deltacomputer = deltautil.deltacomputer(self)
2167 # loop through our set of deltas
2155 # loop through our set of deltas
2168 for data in deltas:
2156 for data in deltas:
2169 node, p1, p2, linknode, deltabase, delta, flags = data
2157 node, p1, p2, linknode, deltabase, delta, flags = data
2170 link = linkmapper(linknode)
2158 link = linkmapper(linknode)
2171 flags = flags or REVIDX_DEFAULT_FLAGS
2159 flags = flags or REVIDX_DEFAULT_FLAGS
2172
2160
2173 nodes.append(node)
2161 nodes.append(node)
2174
2162
2175 if node in self.nodemap:
2163 if node in self.nodemap:
2176 self._nodeduplicatecallback(transaction, node)
2164 self._nodeduplicatecallback(transaction, node)
2177 # this can happen if two branches make the same change
2165 # this can happen if two branches make the same change
2178 continue
2166 continue
2179
2167
2180 for p in (p1, p2):
2168 for p in (p1, p2):
2181 if p not in self.nodemap:
2169 if p not in self.nodemap:
2182 raise error.LookupError(p, self.indexfile,
2170 raise error.LookupError(p, self.indexfile,
2183 _('unknown parent'))
2171 _('unknown parent'))
2184
2172
2185 if deltabase not in self.nodemap:
2173 if deltabase not in self.nodemap:
2186 raise error.LookupError(deltabase, self.indexfile,
2174 raise error.LookupError(deltabase, self.indexfile,
2187 _('unknown delta base'))
2175 _('unknown delta base'))
2188
2176
2189 baserev = self.rev(deltabase)
2177 baserev = self.rev(deltabase)
2190
2178
2191 if baserev != nullrev and self.iscensored(baserev):
2179 if baserev != nullrev and self.iscensored(baserev):
2192 # if base is censored, delta must be full replacement in a
2180 # if base is censored, delta must be full replacement in a
2193 # single patch operation
2181 # single patch operation
2194 hlen = struct.calcsize(">lll")
2182 hlen = struct.calcsize(">lll")
2195 oldlen = self.rawsize(baserev)
2183 oldlen = self.rawsize(baserev)
2196 newlen = len(delta) - hlen
2184 newlen = len(delta) - hlen
2197 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2185 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2198 raise error.CensoredBaseError(self.indexfile,
2186 raise error.CensoredBaseError(self.indexfile,
2199 self.node(baserev))
2187 self.node(baserev))
2200
2188
2201 if not flags and self._peek_iscensored(baserev, delta, flush):
2189 if not flags and self._peek_iscensored(baserev, delta, flush):
2202 flags |= REVIDX_ISCENSORED
2190 flags |= REVIDX_ISCENSORED
2203
2191
2204 # We assume consumers of addrevisioncb will want to retrieve
2192 # We assume consumers of addrevisioncb will want to retrieve
2205 # the added revision, which will require a call to
2193 # the added revision, which will require a call to
2206 # revision(). revision() will fast path if there is a cache
2194 # revision(). revision() will fast path if there is a cache
2207 # hit. So, we tell _addrevision() to always cache in this case.
2195 # hit. So, we tell _addrevision() to always cache in this case.
2208 # We're only using addgroup() in the context of changegroup
2196 # We're only using addgroup() in the context of changegroup
2209 # generation so the revision data can always be handled as raw
2197 # generation so the revision data can always be handled as raw
2210 # by the flagprocessor.
2198 # by the flagprocessor.
2211 self._addrevision(node, None, transaction, link,
2199 self._addrevision(node, None, transaction, link,
2212 p1, p2, flags, (baserev, delta),
2200 p1, p2, flags, (baserev, delta),
2213 ifh, dfh,
2201 ifh, dfh,
2214 alwayscache=bool(addrevisioncb),
2202 alwayscache=bool(addrevisioncb),
2215 deltacomputer=deltacomputer)
2203 deltacomputer=deltacomputer)
2216
2204
2217 if addrevisioncb:
2205 if addrevisioncb:
2218 addrevisioncb(self, node)
2206 addrevisioncb(self, node)
2219
2207
2220 if not dfh and not self._inline:
2208 if not dfh and not self._inline:
2221 # addrevision switched from inline to conventional
2209 # addrevision switched from inline to conventional
2222 # reopen the index
2210 # reopen the index
2223 ifh.close()
2211 ifh.close()
2224 dfh = self._datafp("a+")
2212 dfh = self._datafp("a+")
2225 ifh = self._indexfp("a+")
2213 ifh = self._indexfp("a+")
2226 self._writinghandles = (ifh, dfh)
2214 self._writinghandles = (ifh, dfh)
2227 finally:
2215 finally:
2228 self._writinghandles = None
2216 self._writinghandles = None
2229
2217
2230 if dfh:
2218 if dfh:
2231 dfh.close()
2219 dfh.close()
2232 ifh.close()
2220 ifh.close()
2233
2221
2234 return nodes
2222 return nodes
2235
2223
2236 def iscensored(self, rev):
2224 def iscensored(self, rev):
2237 """Check if a file revision is censored."""
2225 """Check if a file revision is censored."""
2238 if not self._censorable:
2226 if not self._censorable:
2239 return False
2227 return False
2240
2228
2241 return self.flags(rev) & REVIDX_ISCENSORED
2229 return self.flags(rev) & REVIDX_ISCENSORED
2242
2230
2243 def _peek_iscensored(self, baserev, delta, flush):
2231 def _peek_iscensored(self, baserev, delta, flush):
2244 """Quickly check if a delta produces a censored revision."""
2232 """Quickly check if a delta produces a censored revision."""
2245 if not self._censorable:
2233 if not self._censorable:
2246 return False
2234 return False
2247
2235
2248 return storageutil.deltaiscensored(delta, baserev, self.rawsize)
2236 return storageutil.deltaiscensored(delta, baserev, self.rawsize)
2249
2237
2250 def getstrippoint(self, minlink):
2238 def getstrippoint(self, minlink):
2251 """find the minimum rev that must be stripped to strip the linkrev
2239 """find the minimum rev that must be stripped to strip the linkrev
2252
2240
2253 Returns a tuple containing the minimum rev and a set of all revs that
2241 Returns a tuple containing the minimum rev and a set of all revs that
2254 have linkrevs that will be broken by this strip.
2242 have linkrevs that will be broken by this strip.
2255 """
2243 """
2256 return storageutil.resolvestripinfo(minlink, len(self) - 1,
2244 return storageutil.resolvestripinfo(minlink, len(self) - 1,
2257 self.headrevs(),
2245 self.headrevs(),
2258 self.linkrev, self.parentrevs)
2246 self.linkrev, self.parentrevs)
2259
2247
2260 def strip(self, minlink, transaction):
2248 def strip(self, minlink, transaction):
2261 """truncate the revlog on the first revision with a linkrev >= minlink
2249 """truncate the revlog on the first revision with a linkrev >= minlink
2262
2250
2263 This function is called when we're stripping revision minlink and
2251 This function is called when we're stripping revision minlink and
2264 its descendants from the repository.
2252 its descendants from the repository.
2265
2253
2266 We have to remove all revisions with linkrev >= minlink, because
2254 We have to remove all revisions with linkrev >= minlink, because
2267 the equivalent changelog revisions will be renumbered after the
2255 the equivalent changelog revisions will be renumbered after the
2268 strip.
2256 strip.
2269
2257
2270 So we truncate the revlog on the first of these revisions, and
2258 So we truncate the revlog on the first of these revisions, and
2271 trust that the caller has saved the revisions that shouldn't be
2259 trust that the caller has saved the revisions that shouldn't be
2272 removed and that it'll re-add them after this truncation.
2260 removed and that it'll re-add them after this truncation.
2273 """
2261 """
2274 if len(self) == 0:
2262 if len(self) == 0:
2275 return
2263 return
2276
2264
2277 rev, _ = self.getstrippoint(minlink)
2265 rev, _ = self.getstrippoint(minlink)
2278 if rev == len(self):
2266 if rev == len(self):
2279 return
2267 return
2280
2268
2281 # first truncate the files on disk
2269 # first truncate the files on disk
2282 end = self.start(rev)
2270 end = self.start(rev)
2283 if not self._inline:
2271 if not self._inline:
2284 transaction.add(self.datafile, end)
2272 transaction.add(self.datafile, end)
2285 end = rev * self._io.size
2273 end = rev * self._io.size
2286 else:
2274 else:
2287 end += rev * self._io.size
2275 end += rev * self._io.size
2288
2276
2289 transaction.add(self.indexfile, end)
2277 transaction.add(self.indexfile, end)
2290
2278
2291 # then reset internal state in memory to forget those revisions
2279 # then reset internal state in memory to forget those revisions
2292 self._revisioncache = None
2280 self._revisioncache = None
2293 self._chaininfocache = {}
2281 self._chaininfocache = {}
2294 self._chunkclear()
2282 self._chunkclear()
2295 for x in pycompat.xrange(rev, len(self)):
2283 for x in pycompat.xrange(rev, len(self)):
2296 del self.nodemap[self.node(x)]
2284 del self.nodemap[self.node(x)]
2297
2285
2298 del self.index[rev:-1]
2286 del self.index[rev:-1]
2299 self._nodepos = None
2287 self._nodepos = None
2300
2288
2301 def checksize(self):
2289 def checksize(self):
2302 """Check size of index and data files
2290 """Check size of index and data files
2303
2291
2304 return a (dd, di) tuple.
2292 return a (dd, di) tuple.
2305 - dd: extra bytes for the "data" file
2293 - dd: extra bytes for the "data" file
2306 - di: extra bytes for the "index" file
2294 - di: extra bytes for the "index" file
2307
2295
2308 A healthy revlog will return (0, 0).
2296 A healthy revlog will return (0, 0).
2309 """
2297 """
2310 expected = 0
2298 expected = 0
2311 if len(self):
2299 if len(self):
2312 expected = max(0, self.end(len(self) - 1))
2300 expected = max(0, self.end(len(self) - 1))
2313
2301
2314 try:
2302 try:
2315 with self._datafp() as f:
2303 with self._datafp() as f:
2316 f.seek(0, io.SEEK_END)
2304 f.seek(0, io.SEEK_END)
2317 actual = f.tell()
2305 actual = f.tell()
2318 dd = actual - expected
2306 dd = actual - expected
2319 except IOError as inst:
2307 except IOError as inst:
2320 if inst.errno != errno.ENOENT:
2308 if inst.errno != errno.ENOENT:
2321 raise
2309 raise
2322 dd = 0
2310 dd = 0
2323
2311
2324 try:
2312 try:
2325 f = self.opener(self.indexfile)
2313 f = self.opener(self.indexfile)
2326 f.seek(0, io.SEEK_END)
2314 f.seek(0, io.SEEK_END)
2327 actual = f.tell()
2315 actual = f.tell()
2328 f.close()
2316 f.close()
2329 s = self._io.size
2317 s = self._io.size
2330 i = max(0, actual // s)
2318 i = max(0, actual // s)
2331 di = actual - (i * s)
2319 di = actual - (i * s)
2332 if self._inline:
2320 if self._inline:
2333 databytes = 0
2321 databytes = 0
2334 for r in self:
2322 for r in self:
2335 databytes += max(0, self.length(r))
2323 databytes += max(0, self.length(r))
2336 dd = 0
2324 dd = 0
2337 di = actual - len(self) * s - databytes
2325 di = actual - len(self) * s - databytes
2338 except IOError as inst:
2326 except IOError as inst:
2339 if inst.errno != errno.ENOENT:
2327 if inst.errno != errno.ENOENT:
2340 raise
2328 raise
2341 di = 0
2329 di = 0
2342
2330
2343 return (dd, di)
2331 return (dd, di)
2344
2332
2345 def files(self):
2333 def files(self):
2346 res = [self.indexfile]
2334 res = [self.indexfile]
2347 if not self._inline:
2335 if not self._inline:
2348 res.append(self.datafile)
2336 res.append(self.datafile)
2349 return res
2337 return res
2350
2338
2351 def emitrevisions(self, nodes, nodesorder=None, revisiondata=False,
2339 def emitrevisions(self, nodes, nodesorder=None, revisiondata=False,
2352 assumehaveparentrevisions=False,
2340 assumehaveparentrevisions=False,
2353 deltamode=repository.CG_DELTAMODE_STD):
2341 deltamode=repository.CG_DELTAMODE_STD):
2354 if nodesorder not in ('nodes', 'storage', 'linear', None):
2342 if nodesorder not in ('nodes', 'storage', 'linear', None):
2355 raise error.ProgrammingError('unhandled value for nodesorder: %s' %
2343 raise error.ProgrammingError('unhandled value for nodesorder: %s' %
2356 nodesorder)
2344 nodesorder)
2357
2345
2358 if nodesorder is None and not self._generaldelta:
2346 if nodesorder is None and not self._generaldelta:
2359 nodesorder = 'storage'
2347 nodesorder = 'storage'
2360
2348
2361 if (not self._storedeltachains and
2349 if (not self._storedeltachains and
2362 deltamode != repository.CG_DELTAMODE_PREV):
2350 deltamode != repository.CG_DELTAMODE_PREV):
2363 deltamode = repository.CG_DELTAMODE_FULL
2351 deltamode = repository.CG_DELTAMODE_FULL
2364
2352
2365 return storageutil.emitrevisions(
2353 return storageutil.emitrevisions(
2366 self, nodes, nodesorder, revlogrevisiondelta,
2354 self, nodes, nodesorder, revlogrevisiondelta,
2367 deltaparentfn=self.deltaparent,
2355 deltaparentfn=self.deltaparent,
2368 candeltafn=self.candelta,
2356 candeltafn=self.candelta,
2369 rawsizefn=self.rawsize,
2357 rawsizefn=self.rawsize,
2370 revdifffn=self.revdiff,
2358 revdifffn=self.revdiff,
2371 flagsfn=self.flags,
2359 flagsfn=self.flags,
2372 deltamode=deltamode,
2360 deltamode=deltamode,
2373 revisiondata=revisiondata,
2361 revisiondata=revisiondata,
2374 assumehaveparentrevisions=assumehaveparentrevisions)
2362 assumehaveparentrevisions=assumehaveparentrevisions)
2375
2363
2376 DELTAREUSEALWAYS = 'always'
2364 DELTAREUSEALWAYS = 'always'
2377 DELTAREUSESAMEREVS = 'samerevs'
2365 DELTAREUSESAMEREVS = 'samerevs'
2378 DELTAREUSENEVER = 'never'
2366 DELTAREUSENEVER = 'never'
2379
2367
2380 DELTAREUSEFULLADD = 'fulladd'
2368 DELTAREUSEFULLADD = 'fulladd'
2381
2369
2382 DELTAREUSEALL = {'always', 'samerevs', 'never', 'fulladd'}
2370 DELTAREUSEALL = {'always', 'samerevs', 'never', 'fulladd'}
2383
2371
2384 def clone(self, tr, destrevlog, addrevisioncb=None,
2372 def clone(self, tr, destrevlog, addrevisioncb=None,
2385 deltareuse=DELTAREUSESAMEREVS, forcedeltabothparents=None):
2373 deltareuse=DELTAREUSESAMEREVS, forcedeltabothparents=None):
2386 """Copy this revlog to another, possibly with format changes.
2374 """Copy this revlog to another, possibly with format changes.
2387
2375
2388 The destination revlog will contain the same revisions and nodes.
2376 The destination revlog will contain the same revisions and nodes.
2389 However, it may not be bit-for-bit identical due to e.g. delta encoding
2377 However, it may not be bit-for-bit identical due to e.g. delta encoding
2390 differences.
2378 differences.
2391
2379
2392 The ``deltareuse`` argument control how deltas from the existing revlog
2380 The ``deltareuse`` argument control how deltas from the existing revlog
2393 are preserved in the destination revlog. The argument can have the
2381 are preserved in the destination revlog. The argument can have the
2394 following values:
2382 following values:
2395
2383
2396 DELTAREUSEALWAYS
2384 DELTAREUSEALWAYS
2397 Deltas will always be reused (if possible), even if the destination
2385 Deltas will always be reused (if possible), even if the destination
2398 revlog would not select the same revisions for the delta. This is the
2386 revlog would not select the same revisions for the delta. This is the
2399 fastest mode of operation.
2387 fastest mode of operation.
2400 DELTAREUSESAMEREVS
2388 DELTAREUSESAMEREVS
2401 Deltas will be reused if the destination revlog would pick the same
2389 Deltas will be reused if the destination revlog would pick the same
2402 revisions for the delta. This mode strikes a balance between speed
2390 revisions for the delta. This mode strikes a balance between speed
2403 and optimization.
2391 and optimization.
2404 DELTAREUSENEVER
2392 DELTAREUSENEVER
2405 Deltas will never be reused. This is the slowest mode of execution.
2393 Deltas will never be reused. This is the slowest mode of execution.
2406 This mode can be used to recompute deltas (e.g. if the diff/delta
2394 This mode can be used to recompute deltas (e.g. if the diff/delta
2407 algorithm changes).
2395 algorithm changes).
2408
2396
2409 Delta computation can be slow, so the choice of delta reuse policy can
2397 Delta computation can be slow, so the choice of delta reuse policy can
2410 significantly affect run time.
2398 significantly affect run time.
2411
2399
2412 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2400 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2413 two extremes. Deltas will be reused if they are appropriate. But if the
2401 two extremes. Deltas will be reused if they are appropriate. But if the
2414 delta could choose a better revision, it will do so. This means if you
2402 delta could choose a better revision, it will do so. This means if you
2415 are converting a non-generaldelta revlog to a generaldelta revlog,
2403 are converting a non-generaldelta revlog to a generaldelta revlog,
2416 deltas will be recomputed if the delta's parent isn't a parent of the
2404 deltas will be recomputed if the delta's parent isn't a parent of the
2417 revision.
2405 revision.
2418
2406
2419 In addition to the delta policy, the ``forcedeltabothparents``
2407 In addition to the delta policy, the ``forcedeltabothparents``
2420 argument controls whether to force compute deltas against both parents
2408 argument controls whether to force compute deltas against both parents
2421 for merges. By default, the current default is used.
2409 for merges. By default, the current default is used.
2422 """
2410 """
2423 if deltareuse not in self.DELTAREUSEALL:
2411 if deltareuse not in self.DELTAREUSEALL:
2424 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2412 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2425
2413
2426 if len(destrevlog):
2414 if len(destrevlog):
2427 raise ValueError(_('destination revlog is not empty'))
2415 raise ValueError(_('destination revlog is not empty'))
2428
2416
2429 if getattr(self, 'filteredrevs', None):
2417 if getattr(self, 'filteredrevs', None):
2430 raise ValueError(_('source revlog has filtered revisions'))
2418 raise ValueError(_('source revlog has filtered revisions'))
2431 if getattr(destrevlog, 'filteredrevs', None):
2419 if getattr(destrevlog, 'filteredrevs', None):
2432 raise ValueError(_('destination revlog has filtered revisions'))
2420 raise ValueError(_('destination revlog has filtered revisions'))
2433
2421
2434 # lazydelta and lazydeltabase controls whether to reuse a cached delta,
2422 # lazydelta and lazydeltabase controls whether to reuse a cached delta,
2435 # if possible.
2423 # if possible.
2436 oldlazydelta = destrevlog._lazydelta
2424 oldlazydelta = destrevlog._lazydelta
2437 oldlazydeltabase = destrevlog._lazydeltabase
2425 oldlazydeltabase = destrevlog._lazydeltabase
2438 oldamd = destrevlog._deltabothparents
2426 oldamd = destrevlog._deltabothparents
2439
2427
2440 try:
2428 try:
2441 if deltareuse == self.DELTAREUSEALWAYS:
2429 if deltareuse == self.DELTAREUSEALWAYS:
2442 destrevlog._lazydeltabase = True
2430 destrevlog._lazydeltabase = True
2443 destrevlog._lazydelta = True
2431 destrevlog._lazydelta = True
2444 elif deltareuse == self.DELTAREUSESAMEREVS:
2432 elif deltareuse == self.DELTAREUSESAMEREVS:
2445 destrevlog._lazydeltabase = False
2433 destrevlog._lazydeltabase = False
2446 destrevlog._lazydelta = True
2434 destrevlog._lazydelta = True
2447 elif deltareuse == self.DELTAREUSENEVER:
2435 elif deltareuse == self.DELTAREUSENEVER:
2448 destrevlog._lazydeltabase = False
2436 destrevlog._lazydeltabase = False
2449 destrevlog._lazydelta = False
2437 destrevlog._lazydelta = False
2450
2438
2451 destrevlog._deltabothparents = forcedeltabothparents or oldamd
2439 destrevlog._deltabothparents = forcedeltabothparents or oldamd
2452
2440
2453 deltacomputer = deltautil.deltacomputer(destrevlog)
2441 deltacomputer = deltautil.deltacomputer(destrevlog)
2454 index = self.index
2442 index = self.index
2455 for rev in self:
2443 for rev in self:
2456 entry = index[rev]
2444 entry = index[rev]
2457
2445
2458 # Some classes override linkrev to take filtered revs into
2446 # Some classes override linkrev to take filtered revs into
2459 # account. Use raw entry from index.
2447 # account. Use raw entry from index.
2460 flags = entry[0] & 0xffff
2448 flags = entry[0] & 0xffff
2461 linkrev = entry[4]
2449 linkrev = entry[4]
2462 p1 = index[entry[5]][7]
2450 p1 = index[entry[5]][7]
2463 p2 = index[entry[6]][7]
2451 p2 = index[entry[6]][7]
2464 node = entry[7]
2452 node = entry[7]
2465
2453
2466 # (Possibly) reuse the delta from the revlog if allowed and
2454 # (Possibly) reuse the delta from the revlog if allowed and
2467 # the revlog chunk is a delta.
2455 # the revlog chunk is a delta.
2468 cachedelta = None
2456 cachedelta = None
2469 rawtext = None
2457 rawtext = None
2470 if (deltareuse != self.DELTAREUSEFULLADD
2458 if (deltareuse != self.DELTAREUSEFULLADD
2471 and destrevlog._lazydelta):
2459 and destrevlog._lazydelta):
2472 dp = self.deltaparent(rev)
2460 dp = self.deltaparent(rev)
2473 if dp != nullrev:
2461 if dp != nullrev:
2474 cachedelta = (dp, bytes(self._chunk(rev)))
2462 cachedelta = (dp, bytes(self._chunk(rev)))
2475
2463
2476 if not cachedelta:
2464 if not cachedelta:
2477 rawtext = self.revision(rev, raw=True)
2465 rawtext = self.revision(rev, raw=True)
2478
2466
2479
2467
2480 if deltareuse == self.DELTAREUSEFULLADD:
2468 if deltareuse == self.DELTAREUSEFULLADD:
2481 destrevlog.addrevision(rawtext, tr, linkrev, p1, p2,
2469 destrevlog.addrevision(rawtext, tr, linkrev, p1, p2,
2482 cachedelta=cachedelta,
2470 cachedelta=cachedelta,
2483 node=node, flags=flags,
2471 node=node, flags=flags,
2484 deltacomputer=deltacomputer)
2472 deltacomputer=deltacomputer)
2485 else:
2473 else:
2486 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2474 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2487 checkambig=False)
2475 checkambig=False)
2488 dfh = None
2476 dfh = None
2489 if not destrevlog._inline:
2477 if not destrevlog._inline:
2490 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2478 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2491 try:
2479 try:
2492 destrevlog._addrevision(node, rawtext, tr, linkrev, p1,
2480 destrevlog._addrevision(node, rawtext, tr, linkrev, p1,
2493 p2, flags, cachedelta, ifh, dfh,
2481 p2, flags, cachedelta, ifh, dfh,
2494 deltacomputer=deltacomputer)
2482 deltacomputer=deltacomputer)
2495 finally:
2483 finally:
2496 if dfh:
2484 if dfh:
2497 dfh.close()
2485 dfh.close()
2498 ifh.close()
2486 ifh.close()
2499
2487
2500 if addrevisioncb:
2488 if addrevisioncb:
2501 addrevisioncb(self, rev, node)
2489 addrevisioncb(self, rev, node)
2502 finally:
2490 finally:
2503 destrevlog._lazydelta = oldlazydelta
2491 destrevlog._lazydelta = oldlazydelta
2504 destrevlog._lazydeltabase = oldlazydeltabase
2492 destrevlog._lazydeltabase = oldlazydeltabase
2505 destrevlog._deltabothparents = oldamd
2493 destrevlog._deltabothparents = oldamd
2506
2494
2507 def censorrevision(self, tr, censornode, tombstone=b''):
2495 def censorrevision(self, tr, censornode, tombstone=b''):
2508 if (self.version & 0xFFFF) == REVLOGV0:
2496 if (self.version & 0xFFFF) == REVLOGV0:
2509 raise error.RevlogError(_('cannot censor with version %d revlogs') %
2497 raise error.RevlogError(_('cannot censor with version %d revlogs') %
2510 self.version)
2498 self.version)
2511
2499
2512 censorrev = self.rev(censornode)
2500 censorrev = self.rev(censornode)
2513 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
2501 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
2514
2502
2515 if len(tombstone) > self.rawsize(censorrev):
2503 if len(tombstone) > self.rawsize(censorrev):
2516 raise error.Abort(_('censor tombstone must be no longer than '
2504 raise error.Abort(_('censor tombstone must be no longer than '
2517 'censored data'))
2505 'censored data'))
2518
2506
2519 # Rewriting the revlog in place is hard. Our strategy for censoring is
2507 # Rewriting the revlog in place is hard. Our strategy for censoring is
2520 # to create a new revlog, copy all revisions to it, then replace the
2508 # to create a new revlog, copy all revisions to it, then replace the
2521 # revlogs on transaction close.
2509 # revlogs on transaction close.
2522
2510
2523 newindexfile = self.indexfile + b'.tmpcensored'
2511 newindexfile = self.indexfile + b'.tmpcensored'
2524 newdatafile = self.datafile + b'.tmpcensored'
2512 newdatafile = self.datafile + b'.tmpcensored'
2525
2513
2526 # This is a bit dangerous. We could easily have a mismatch of state.
2514 # This is a bit dangerous. We could easily have a mismatch of state.
2527 newrl = revlog(self.opener, newindexfile, newdatafile,
2515 newrl = revlog(self.opener, newindexfile, newdatafile,
2528 censorable=True)
2516 censorable=True)
2529 newrl.version = self.version
2517 newrl.version = self.version
2530 newrl._generaldelta = self._generaldelta
2518 newrl._generaldelta = self._generaldelta
2531 newrl._io = self._io
2519 newrl._io = self._io
2532
2520
2533 for rev in self.revs():
2521 for rev in self.revs():
2534 node = self.node(rev)
2522 node = self.node(rev)
2535 p1, p2 = self.parents(node)
2523 p1, p2 = self.parents(node)
2536
2524
2537 if rev == censorrev:
2525 if rev == censorrev:
2538 newrl.addrawrevision(tombstone, tr, self.linkrev(censorrev),
2526 newrl.addrawrevision(tombstone, tr, self.linkrev(censorrev),
2539 p1, p2, censornode, REVIDX_ISCENSORED)
2527 p1, p2, censornode, REVIDX_ISCENSORED)
2540
2528
2541 if newrl.deltaparent(rev) != nullrev:
2529 if newrl.deltaparent(rev) != nullrev:
2542 raise error.Abort(_('censored revision stored as delta; '
2530 raise error.Abort(_('censored revision stored as delta; '
2543 'cannot censor'),
2531 'cannot censor'),
2544 hint=_('censoring of revlogs is not '
2532 hint=_('censoring of revlogs is not '
2545 'fully implemented; please report '
2533 'fully implemented; please report '
2546 'this bug'))
2534 'this bug'))
2547 continue
2535 continue
2548
2536
2549 if self.iscensored(rev):
2537 if self.iscensored(rev):
2550 if self.deltaparent(rev) != nullrev:
2538 if self.deltaparent(rev) != nullrev:
2551 raise error.Abort(_('cannot censor due to censored '
2539 raise error.Abort(_('cannot censor due to censored '
2552 'revision having delta stored'))
2540 'revision having delta stored'))
2553 rawtext = self._chunk(rev)
2541 rawtext = self._chunk(rev)
2554 else:
2542 else:
2555 rawtext = self.revision(rev, raw=True)
2543 rawtext = self.revision(rev, raw=True)
2556
2544
2557 newrl.addrawrevision(rawtext, tr, self.linkrev(rev), p1, p2, node,
2545 newrl.addrawrevision(rawtext, tr, self.linkrev(rev), p1, p2, node,
2558 self.flags(rev))
2546 self.flags(rev))
2559
2547
2560 tr.addbackup(self.indexfile, location='store')
2548 tr.addbackup(self.indexfile, location='store')
2561 if not self._inline:
2549 if not self._inline:
2562 tr.addbackup(self.datafile, location='store')
2550 tr.addbackup(self.datafile, location='store')
2563
2551
2564 self.opener.rename(newrl.indexfile, self.indexfile)
2552 self.opener.rename(newrl.indexfile, self.indexfile)
2565 if not self._inline:
2553 if not self._inline:
2566 self.opener.rename(newrl.datafile, self.datafile)
2554 self.opener.rename(newrl.datafile, self.datafile)
2567
2555
2568 self.clearcaches()
2556 self.clearcaches()
2569 self._loadindex()
2557 self._loadindex()
2570
2558
2571 def verifyintegrity(self, state):
2559 def verifyintegrity(self, state):
2572 """Verifies the integrity of the revlog.
2560 """Verifies the integrity of the revlog.
2573
2561
2574 Yields ``revlogproblem`` instances describing problems that are
2562 Yields ``revlogproblem`` instances describing problems that are
2575 found.
2563 found.
2576 """
2564 """
2577 dd, di = self.checksize()
2565 dd, di = self.checksize()
2578 if dd:
2566 if dd:
2579 yield revlogproblem(error=_('data length off by %d bytes') % dd)
2567 yield revlogproblem(error=_('data length off by %d bytes') % dd)
2580 if di:
2568 if di:
2581 yield revlogproblem(error=_('index contains %d extra bytes') % di)
2569 yield revlogproblem(error=_('index contains %d extra bytes') % di)
2582
2570
2583 version = self.version & 0xFFFF
2571 version = self.version & 0xFFFF
2584
2572
2585 # The verifier tells us what version revlog we should be.
2573 # The verifier tells us what version revlog we should be.
2586 if version != state['expectedversion']:
2574 if version != state['expectedversion']:
2587 yield revlogproblem(
2575 yield revlogproblem(
2588 warning=_("warning: '%s' uses revlog format %d; expected %d") %
2576 warning=_("warning: '%s' uses revlog format %d; expected %d") %
2589 (self.indexfile, version, state['expectedversion']))
2577 (self.indexfile, version, state['expectedversion']))
2590
2578
2591 state['skipread'] = set()
2579 state['skipread'] = set()
2592
2580
2593 for rev in self:
2581 for rev in self:
2594 node = self.node(rev)
2582 node = self.node(rev)
2595
2583
2596 # Verify contents. 4 cases to care about:
2584 # Verify contents. 4 cases to care about:
2597 #
2585 #
2598 # common: the most common case
2586 # common: the most common case
2599 # rename: with a rename
2587 # rename: with a rename
2600 # meta: file content starts with b'\1\n', the metadata
2588 # meta: file content starts with b'\1\n', the metadata
2601 # header defined in filelog.py, but without a rename
2589 # header defined in filelog.py, but without a rename
2602 # ext: content stored externally
2590 # ext: content stored externally
2603 #
2591 #
2604 # More formally, their differences are shown below:
2592 # More formally, their differences are shown below:
2605 #
2593 #
2606 # | common | rename | meta | ext
2594 # | common | rename | meta | ext
2607 # -------------------------------------------------------
2595 # -------------------------------------------------------
2608 # flags() | 0 | 0 | 0 | not 0
2596 # flags() | 0 | 0 | 0 | not 0
2609 # renamed() | False | True | False | ?
2597 # renamed() | False | True | False | ?
2610 # rawtext[0:2]=='\1\n'| False | True | True | ?
2598 # rawtext[0:2]=='\1\n'| False | True | True | ?
2611 #
2599 #
2612 # "rawtext" means the raw text stored in revlog data, which
2600 # "rawtext" means the raw text stored in revlog data, which
2613 # could be retrieved by "revision(rev, raw=True)". "text"
2601 # could be retrieved by "revision(rev, raw=True)". "text"
2614 # mentioned below is "revision(rev, raw=False)".
2602 # mentioned below is "revision(rev, raw=False)".
2615 #
2603 #
2616 # There are 3 different lengths stored physically:
2604 # There are 3 different lengths stored physically:
2617 # 1. L1: rawsize, stored in revlog index
2605 # 1. L1: rawsize, stored in revlog index
2618 # 2. L2: len(rawtext), stored in revlog data
2606 # 2. L2: len(rawtext), stored in revlog data
2619 # 3. L3: len(text), stored in revlog data if flags==0, or
2607 # 3. L3: len(text), stored in revlog data if flags==0, or
2620 # possibly somewhere else if flags!=0
2608 # possibly somewhere else if flags!=0
2621 #
2609 #
2622 # L1 should be equal to L2. L3 could be different from them.
2610 # L1 should be equal to L2. L3 could be different from them.
2623 # "text" may or may not affect commit hash depending on flag
2611 # "text" may or may not affect commit hash depending on flag
2624 # processors (see revlog.addflagprocessor).
2612 # processors (see revlog.addflagprocessor).
2625 #
2613 #
2626 # | common | rename | meta | ext
2614 # | common | rename | meta | ext
2627 # -------------------------------------------------
2615 # -------------------------------------------------
2628 # rawsize() | L1 | L1 | L1 | L1
2616 # rawsize() | L1 | L1 | L1 | L1
2629 # size() | L1 | L2-LM | L1(*) | L1 (?)
2617 # size() | L1 | L2-LM | L1(*) | L1 (?)
2630 # len(rawtext) | L2 | L2 | L2 | L2
2618 # len(rawtext) | L2 | L2 | L2 | L2
2631 # len(text) | L2 | L2 | L2 | L3
2619 # len(text) | L2 | L2 | L2 | L3
2632 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
2620 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
2633 #
2621 #
2634 # LM: length of metadata, depending on rawtext
2622 # LM: length of metadata, depending on rawtext
2635 # (*): not ideal, see comment in filelog.size
2623 # (*): not ideal, see comment in filelog.size
2636 # (?): could be "- len(meta)" if the resolved content has
2624 # (?): could be "- len(meta)" if the resolved content has
2637 # rename metadata
2625 # rename metadata
2638 #
2626 #
2639 # Checks needed to be done:
2627 # Checks needed to be done:
2640 # 1. length check: L1 == L2, in all cases.
2628 # 1. length check: L1 == L2, in all cases.
2641 # 2. hash check: depending on flag processor, we may need to
2629 # 2. hash check: depending on flag processor, we may need to
2642 # use either "text" (external), or "rawtext" (in revlog).
2630 # use either "text" (external), or "rawtext" (in revlog).
2643
2631
2644 try:
2632 try:
2645 skipflags = state.get('skipflags', 0)
2633 skipflags = state.get('skipflags', 0)
2646 if skipflags:
2634 if skipflags:
2647 skipflags &= self.flags(rev)
2635 skipflags &= self.flags(rev)
2648
2636
2649 if skipflags:
2637 if skipflags:
2650 state['skipread'].add(node)
2638 state['skipread'].add(node)
2651 else:
2639 else:
2652 # Side-effect: read content and verify hash.
2640 # Side-effect: read content and verify hash.
2653 self.revision(node)
2641 self.revision(node)
2654
2642
2655 l1 = self.rawsize(rev)
2643 l1 = self.rawsize(rev)
2656 l2 = len(self.revision(node, raw=True))
2644 l2 = len(self.revision(node, raw=True))
2657
2645
2658 if l1 != l2:
2646 if l1 != l2:
2659 yield revlogproblem(
2647 yield revlogproblem(
2660 error=_('unpacked size is %d, %d expected') % (l2, l1),
2648 error=_('unpacked size is %d, %d expected') % (l2, l1),
2661 node=node)
2649 node=node)
2662
2650
2663 except error.CensoredNodeError:
2651 except error.CensoredNodeError:
2664 if state['erroroncensored']:
2652 if state['erroroncensored']:
2665 yield revlogproblem(error=_('censored file data'),
2653 yield revlogproblem(error=_('censored file data'),
2666 node=node)
2654 node=node)
2667 state['skipread'].add(node)
2655 state['skipread'].add(node)
2668 except Exception as e:
2656 except Exception as e:
2669 yield revlogproblem(
2657 yield revlogproblem(
2670 error=_('unpacking %s: %s') % (short(node),
2658 error=_('unpacking %s: %s') % (short(node),
2671 stringutil.forcebytestr(e)),
2659 stringutil.forcebytestr(e)),
2672 node=node)
2660 node=node)
2673 state['skipread'].add(node)
2661 state['skipread'].add(node)
2674
2662
2675 def storageinfo(self, exclusivefiles=False, sharedfiles=False,
2663 def storageinfo(self, exclusivefiles=False, sharedfiles=False,
2676 revisionscount=False, trackedsize=False,
2664 revisionscount=False, trackedsize=False,
2677 storedsize=False):
2665 storedsize=False):
2678 d = {}
2666 d = {}
2679
2667
2680 if exclusivefiles:
2668 if exclusivefiles:
2681 d['exclusivefiles'] = [(self.opener, self.indexfile)]
2669 d['exclusivefiles'] = [(self.opener, self.indexfile)]
2682 if not self._inline:
2670 if not self._inline:
2683 d['exclusivefiles'].append((self.opener, self.datafile))
2671 d['exclusivefiles'].append((self.opener, self.datafile))
2684
2672
2685 if sharedfiles:
2673 if sharedfiles:
2686 d['sharedfiles'] = []
2674 d['sharedfiles'] = []
2687
2675
2688 if revisionscount:
2676 if revisionscount:
2689 d['revisionscount'] = len(self)
2677 d['revisionscount'] = len(self)
2690
2678
2691 if trackedsize:
2679 if trackedsize:
2692 d['trackedsize'] = sum(map(self.rawsize, iter(self)))
2680 d['trackedsize'] = sum(map(self.rawsize, iter(self)))
2693
2681
2694 if storedsize:
2682 if storedsize:
2695 d['storedsize'] = sum(self.opener.stat(path).st_size
2683 d['storedsize'] = sum(self.opener.stat(path).st_size
2696 for path in self.files())
2684 for path in self.files())
2697
2685
2698 return d
2686 return d
@@ -1,39 +1,53 b''
1 # flagutils.py - code to deal with revlog flags and their processors
1 # flagutils.py - code to deal with revlog flags and their processors
2 #
2 #
3 # Copyright 2016 Remi Chaintron <remi@fb.com>
3 # Copyright 2016 Remi Chaintron <remi@fb.com>
4 # Copyright 2016-2019 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 # Copyright 2016-2019 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
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 from ..i18n import _
12
11 from .constants import (
13 from .constants import (
12 REVIDX_DEFAULT_FLAGS,
14 REVIDX_DEFAULT_FLAGS,
13 REVIDX_ELLIPSIS,
15 REVIDX_ELLIPSIS,
14 REVIDX_EXTSTORED,
16 REVIDX_EXTSTORED,
15 REVIDX_FLAGS_ORDER,
17 REVIDX_FLAGS_ORDER,
16 REVIDX_ISCENSORED,
18 REVIDX_ISCENSORED,
17 REVIDX_RAWTEXT_CHANGING_FLAGS,
19 REVIDX_RAWTEXT_CHANGING_FLAGS,
18 )
20 )
19
21
20 from .. import (
22 from .. import (
23 error,
21 util
24 util
22 )
25 )
23
26
24 # blanked usage of all the name to prevent pyflakes constraints
27 # blanked usage of all the name to prevent pyflakes constraints
25 # We need these name available in the module for extensions.
28 # We need these name available in the module for extensions.
26 REVIDX_ISCENSORED
29 REVIDX_ISCENSORED
27 REVIDX_ELLIPSIS
30 REVIDX_ELLIPSIS
28 REVIDX_EXTSTORED
31 REVIDX_EXTSTORED
29 REVIDX_DEFAULT_FLAGS
32 REVIDX_DEFAULT_FLAGS
30 REVIDX_FLAGS_ORDER
33 REVIDX_FLAGS_ORDER
31 REVIDX_RAWTEXT_CHANGING_FLAGS
34 REVIDX_RAWTEXT_CHANGING_FLAGS
32
35
33 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
36 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
34
37
35 # Store flag processors (cf. 'addflagprocessor()' to register)
38 # Store flag processors (cf. 'addflagprocessor()' to register)
36 flagprocessors = {
39 flagprocessors = {
37 REVIDX_ISCENSORED: None,
40 REVIDX_ISCENSORED: None,
38 }
41 }
39
42
43 def insertflagprocessor(flag, processor, flagprocessors):
44 if not flag & REVIDX_KNOWN_FLAGS:
45 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
46 raise error.ProgrammingError(msg)
47 if flag not in REVIDX_FLAGS_ORDER:
48 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
49 raise error.ProgrammingError(msg)
50 if flag in flagprocessors:
51 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
52 raise error.Abort(msg)
53 flagprocessors[flag] = processor
@@ -1,304 +1,304 b''
1 # Create server
1 # Create server
2 $ hg init server
2 $ hg init server
3 $ cd server
3 $ cd server
4 $ cat >> .hg/hgrc << EOF
4 $ cat >> .hg/hgrc << EOF
5 > [extensions]
5 > [extensions]
6 > extension=$TESTDIR/flagprocessorext.py
6 > extension=$TESTDIR/flagprocessorext.py
7 > EOF
7 > EOF
8 $ cd ../
8 $ cd ../
9
9
10 # Clone server and enable extensions
10 # Clone server and enable extensions
11 $ hg clone -q server client
11 $ hg clone -q server client
12 $ cd client
12 $ cd client
13 $ cat >> .hg/hgrc << EOF
13 $ cat >> .hg/hgrc << EOF
14 > [extensions]
14 > [extensions]
15 > extension=$TESTDIR/flagprocessorext.py
15 > extension=$TESTDIR/flagprocessorext.py
16 > EOF
16 > EOF
17
17
18 # Commit file that will trigger the noop extension
18 # Commit file that will trigger the noop extension
19 $ echo '[NOOP]' > noop
19 $ echo '[NOOP]' > noop
20 $ hg commit -Aqm "noop"
20 $ hg commit -Aqm "noop"
21
21
22 # Commit file that will trigger the base64 extension
22 # Commit file that will trigger the base64 extension
23 $ echo '[BASE64]' > base64
23 $ echo '[BASE64]' > base64
24 $ hg commit -Aqm 'base64'
24 $ hg commit -Aqm 'base64'
25
25
26 # Commit file that will trigger the gzip extension
26 # Commit file that will trigger the gzip extension
27 $ echo '[GZIP]' > gzip
27 $ echo '[GZIP]' > gzip
28 $ hg commit -Aqm 'gzip'
28 $ hg commit -Aqm 'gzip'
29
29
30 # Commit file that will trigger noop and base64
30 # Commit file that will trigger noop and base64
31 $ echo '[NOOP][BASE64]' > noop-base64
31 $ echo '[NOOP][BASE64]' > noop-base64
32 $ hg commit -Aqm 'noop+base64'
32 $ hg commit -Aqm 'noop+base64'
33
33
34 # Commit file that will trigger noop and gzip
34 # Commit file that will trigger noop and gzip
35 $ echo '[NOOP][GZIP]' > noop-gzip
35 $ echo '[NOOP][GZIP]' > noop-gzip
36 $ hg commit -Aqm 'noop+gzip'
36 $ hg commit -Aqm 'noop+gzip'
37
37
38 # Commit file that will trigger base64 and gzip
38 # Commit file that will trigger base64 and gzip
39 $ echo '[BASE64][GZIP]' > base64-gzip
39 $ echo '[BASE64][GZIP]' > base64-gzip
40 $ hg commit -Aqm 'base64+gzip'
40 $ hg commit -Aqm 'base64+gzip'
41
41
42 # Commit file that will trigger base64, gzip and noop
42 # Commit file that will trigger base64, gzip and noop
43 $ echo '[BASE64][GZIP][NOOP]' > base64-gzip-noop
43 $ echo '[BASE64][GZIP][NOOP]' > base64-gzip-noop
44 $ hg commit -Aqm 'base64+gzip+noop'
44 $ hg commit -Aqm 'base64+gzip+noop'
45
45
46 # TEST: ensure the revision data is consistent
46 # TEST: ensure the revision data is consistent
47 $ hg cat noop
47 $ hg cat noop
48 [NOOP]
48 [NOOP]
49 $ hg debugdata noop 0
49 $ hg debugdata noop 0
50 [NOOP]
50 [NOOP]
51
51
52 $ hg cat -r . base64
52 $ hg cat -r . base64
53 [BASE64]
53 [BASE64]
54 $ hg debugdata base64 0
54 $ hg debugdata base64 0
55 W0JBU0U2NF0K (no-eol)
55 W0JBU0U2NF0K (no-eol)
56
56
57 $ hg cat -r . gzip
57 $ hg cat -r . gzip
58 [GZIP]
58 [GZIP]
59 $ hg debugdata gzip 0
59 $ hg debugdata gzip 0
60 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
60 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
61
61
62 $ hg cat -r . noop-base64
62 $ hg cat -r . noop-base64
63 [NOOP][BASE64]
63 [NOOP][BASE64]
64 $ hg debugdata noop-base64 0
64 $ hg debugdata noop-base64 0
65 W05PT1BdW0JBU0U2NF0K (no-eol)
65 W05PT1BdW0JBU0U2NF0K (no-eol)
66
66
67 $ hg cat -r . noop-gzip
67 $ hg cat -r . noop-gzip
68 [NOOP][GZIP]
68 [NOOP][GZIP]
69 $ hg debugdata noop-gzip 0
69 $ hg debugdata noop-gzip 0
70 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
70 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
71
71
72 $ hg cat -r . base64-gzip
72 $ hg cat -r . base64-gzip
73 [BASE64][GZIP]
73 [BASE64][GZIP]
74 $ hg debugdata base64-gzip 0
74 $ hg debugdata base64-gzip 0
75 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
75 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
76
76
77 $ hg cat -r . base64-gzip-noop
77 $ hg cat -r . base64-gzip-noop
78 [BASE64][GZIP][NOOP]
78 [BASE64][GZIP][NOOP]
79 $ hg debugdata base64-gzip-noop 0
79 $ hg debugdata base64-gzip-noop 0
80 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
80 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
81
81
82 # Push to the server
82 # Push to the server
83 $ hg push
83 $ hg push
84 pushing to $TESTTMP/server
84 pushing to $TESTTMP/server
85 searching for changes
85 searching for changes
86 adding changesets
86 adding changesets
87 adding manifests
87 adding manifests
88 adding file changes
88 adding file changes
89 added 7 changesets with 7 changes to 7 files
89 added 7 changesets with 7 changes to 7 files
90
90
91 Ensure the data got to the server OK
91 Ensure the data got to the server OK
92
92
93 $ cd ../server
93 $ cd ../server
94 $ hg cat -r 6e48f4215d24 noop
94 $ hg cat -r 6e48f4215d24 noop
95 [NOOP]
95 [NOOP]
96 $ hg debugdata noop 0
96 $ hg debugdata noop 0
97 [NOOP]
97 [NOOP]
98
98
99 $ hg cat -r 6e48f4215d24 base64
99 $ hg cat -r 6e48f4215d24 base64
100 [BASE64]
100 [BASE64]
101 $ hg debugdata base64 0
101 $ hg debugdata base64 0
102 W0JBU0U2NF0K (no-eol)
102 W0JBU0U2NF0K (no-eol)
103
103
104 $ hg cat -r 6e48f4215d24 gzip
104 $ hg cat -r 6e48f4215d24 gzip
105 [GZIP]
105 [GZIP]
106 $ hg debugdata gzip 0
106 $ hg debugdata gzip 0
107 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
107 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
108
108
109 $ hg cat -r 6e48f4215d24 noop-base64
109 $ hg cat -r 6e48f4215d24 noop-base64
110 [NOOP][BASE64]
110 [NOOP][BASE64]
111 $ hg debugdata noop-base64 0
111 $ hg debugdata noop-base64 0
112 W05PT1BdW0JBU0U2NF0K (no-eol)
112 W05PT1BdW0JBU0U2NF0K (no-eol)
113
113
114 $ hg cat -r 6e48f4215d24 noop-gzip
114 $ hg cat -r 6e48f4215d24 noop-gzip
115 [NOOP][GZIP]
115 [NOOP][GZIP]
116 $ hg debugdata noop-gzip 0
116 $ hg debugdata noop-gzip 0
117 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
117 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
118
118
119 $ hg cat -r 6e48f4215d24 base64-gzip
119 $ hg cat -r 6e48f4215d24 base64-gzip
120 [BASE64][GZIP]
120 [BASE64][GZIP]
121 $ hg debugdata base64-gzip 0
121 $ hg debugdata base64-gzip 0
122 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
122 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
123
123
124 $ hg cat -r 6e48f4215d24 base64-gzip-noop
124 $ hg cat -r 6e48f4215d24 base64-gzip-noop
125 [BASE64][GZIP][NOOP]
125 [BASE64][GZIP][NOOP]
126 $ hg debugdata base64-gzip-noop 0
126 $ hg debugdata base64-gzip-noop 0
127 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
127 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
128
128
129 # Initialize new client (not cloning) and setup extension
129 # Initialize new client (not cloning) and setup extension
130 $ cd ..
130 $ cd ..
131 $ hg init client2
131 $ hg init client2
132 $ cd client2
132 $ cd client2
133 $ cat >> .hg/hgrc << EOF
133 $ cat >> .hg/hgrc << EOF
134 > [paths]
134 > [paths]
135 > default = $TESTTMP/server
135 > default = $TESTTMP/server
136 > [extensions]
136 > [extensions]
137 > extension=$TESTDIR/flagprocessorext.py
137 > extension=$TESTDIR/flagprocessorext.py
138 > EOF
138 > EOF
139
139
140 # Pull from server and update to latest revision
140 # Pull from server and update to latest revision
141 $ hg pull default
141 $ hg pull default
142 pulling from $TESTTMP/server
142 pulling from $TESTTMP/server
143 requesting all changes
143 requesting all changes
144 adding changesets
144 adding changesets
145 adding manifests
145 adding manifests
146 adding file changes
146 adding file changes
147 added 7 changesets with 7 changes to 7 files
147 added 7 changesets with 7 changes to 7 files
148 new changesets 07b1b9442c5b:6e48f4215d24
148 new changesets 07b1b9442c5b:6e48f4215d24
149 (run 'hg update' to get a working copy)
149 (run 'hg update' to get a working copy)
150 $ hg update
150 $ hg update
151 7 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 7 files updated, 0 files merged, 0 files removed, 0 files unresolved
152
152
153 # TEST: ensure the revision data is consistent
153 # TEST: ensure the revision data is consistent
154 $ hg cat noop
154 $ hg cat noop
155 [NOOP]
155 [NOOP]
156 $ hg debugdata noop 0
156 $ hg debugdata noop 0
157 [NOOP]
157 [NOOP]
158
158
159 $ hg cat -r . base64
159 $ hg cat -r . base64
160 [BASE64]
160 [BASE64]
161 $ hg debugdata base64 0
161 $ hg debugdata base64 0
162 W0JBU0U2NF0K (no-eol)
162 W0JBU0U2NF0K (no-eol)
163
163
164 $ hg cat -r . gzip
164 $ hg cat -r . gzip
165 [GZIP]
165 [GZIP]
166 $ hg debugdata gzip 0
166 $ hg debugdata gzip 0
167 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
167 x\x9c\x8bv\x8f\xf2\x0c\x88\xe5\x02\x00\x08\xc8\x01\xfd (no-eol) (esc)
168
168
169 $ hg cat -r . noop-base64
169 $ hg cat -r . noop-base64
170 [NOOP][BASE64]
170 [NOOP][BASE64]
171 $ hg debugdata noop-base64 0
171 $ hg debugdata noop-base64 0
172 W05PT1BdW0JBU0U2NF0K (no-eol)
172 W05PT1BdW0JBU0U2NF0K (no-eol)
173
173
174 $ hg cat -r . noop-gzip
174 $ hg cat -r . noop-gzip
175 [NOOP][GZIP]
175 [NOOP][GZIP]
176 $ hg debugdata noop-gzip 0
176 $ hg debugdata noop-gzip 0
177 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
177 x\x9c\x8b\xf6\xf3\xf7\x0f\x88\x8dv\x8f\xf2\x0c\x88\xe5\x02\x00\x1dH\x03\xf1 (no-eol) (esc)
178
178
179 $ hg cat -r . base64-gzip
179 $ hg cat -r . base64-gzip
180 [BASE64][GZIP]
180 [BASE64][GZIP]
181 $ hg debugdata base64-gzip 0
181 $ hg debugdata base64-gzip 0
182 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
182 eJyLdnIMdjUziY12j/IMiOUCACLBBDo= (no-eol)
183
183
184 $ hg cat -r . base64-gzip-noop
184 $ hg cat -r . base64-gzip-noop
185 [BASE64][GZIP][NOOP]
185 [BASE64][GZIP][NOOP]
186 $ hg debugdata base64-gzip-noop 0
186 $ hg debugdata base64-gzip-noop 0
187 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
187 eJyLdnIMdjUziY12j/IMiI328/cPiOUCAESjBi4= (no-eol)
188
188
189 # TEST: ensure a missing processor is handled
189 # TEST: ensure a missing processor is handled
190 $ echo '[FAIL][BASE64][GZIP][NOOP]' > fail-base64-gzip-noop
190 $ echo '[FAIL][BASE64][GZIP][NOOP]' > fail-base64-gzip-noop
191 $ hg commit -Aqm 'fail+base64+gzip+noop'
191 $ hg commit -Aqm 'fail+base64+gzip+noop'
192 abort: missing processor for flag '0x1'!
192 abort: missing processor for flag '0x1'!
193 [255]
193 [255]
194 $ rm fail-base64-gzip-noop
194 $ rm fail-base64-gzip-noop
195
195
196 # TEST: ensure we cannot register several flag processors on the same flag
196 # TEST: ensure we cannot register several flag processors on the same flag
197 $ cat >> .hg/hgrc << EOF
197 $ cat >> .hg/hgrc << EOF
198 > [extensions]
198 > [extensions]
199 > extension=$TESTDIR/flagprocessorext.py
199 > extension=$TESTDIR/flagprocessorext.py
200 > duplicate=$TESTDIR/flagprocessorext.py
200 > duplicate=$TESTDIR/flagprocessorext.py
201 > EOF
201 > EOF
202 $ hg debugrebuilddirstate
202 $ hg debugrebuilddirstate
203 Traceback (most recent call last):
203 Traceback (most recent call last):
204 File "*/mercurial/extensions.py", line *, in _runextsetup (glob)
204 File "*/mercurial/extensions.py", line *, in _runextsetup (glob)
205 extsetup(ui)
205 extsetup(ui)
206 File "*/tests/flagprocessorext.py", line *, in extsetup (glob)
206 File "*/tests/flagprocessorext.py", line *, in extsetup (glob)
207 validatehash,
207 validatehash,
208 File "*/mercurial/revlog.py", line *, in addflagprocessor (glob)
208 File "*/mercurial/revlog.py", line *, in addflagprocessor (glob)
209 _insertflagprocessor(flag, processor, flagutil.flagprocessors)
209 flagutil.insertflagprocessor(flag, processor, flagutil.flagprocessors)
210 File "*/mercurial/revlog.py", line *, in _insertflagprocessor (glob)
210 File "*/mercurial/revlogutils/flagutil.py", line *, in insertflagprocessor (glob)
211 raise error.Abort(msg)
211 raise error.Abort(msg)
212 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
212 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
213 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
213 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
214 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
214 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
215 $ hg st 2>&1 | egrep 'cannot register multiple processors|flagprocessorext'
215 $ hg st 2>&1 | egrep 'cannot register multiple processors|flagprocessorext'
216 File "*/tests/flagprocessorext.py", line *, in extsetup (glob)
216 File "*/tests/flagprocessorext.py", line *, in extsetup (glob)
217 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
217 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
218 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
218 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
219 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
219 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
220 File "*/tests/flagprocessorext.py", line *, in b64decode (glob)
220 File "*/tests/flagprocessorext.py", line *, in b64decode (glob)
221
221
222 $ cd ..
222 $ cd ..
223
223
224 # TEST: bundle repo
224 # TEST: bundle repo
225 $ hg init bundletest
225 $ hg init bundletest
226 $ cd bundletest
226 $ cd bundletest
227
227
228 $ cat >> .hg/hgrc << EOF
228 $ cat >> .hg/hgrc << EOF
229 > [extensions]
229 > [extensions]
230 > flagprocessor=$TESTDIR/flagprocessorext.py
230 > flagprocessor=$TESTDIR/flagprocessorext.py
231 > EOF
231 > EOF
232
232
233 $ for i in 0 single two three 4; do
233 $ for i in 0 single two three 4; do
234 > echo '[BASE64]a-bit-longer-'$i > base64
234 > echo '[BASE64]a-bit-longer-'$i > base64
235 > hg commit -m base64-$i -A base64
235 > hg commit -m base64-$i -A base64
236 > done
236 > done
237
237
238 $ hg update 2 -q
238 $ hg update 2 -q
239 $ echo '[BASE64]a-bit-longer-branching' > base64
239 $ echo '[BASE64]a-bit-longer-branching' > base64
240 $ hg commit -q -m branching
240 $ hg commit -q -m branching
241
241
242 #if repobundlerepo
242 #if repobundlerepo
243 $ hg bundle --base 1 bundle.hg
243 $ hg bundle --base 1 bundle.hg
244 4 changesets found
244 4 changesets found
245 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
245 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
246 $ hg -R bundle.hg log --stat -T '{rev} {desc}\n' base64
246 $ hg -R bundle.hg log --stat -T '{rev} {desc}\n' base64
247 5 branching
247 5 branching
248 base64 | 2 +-
248 base64 | 2 +-
249 1 files changed, 1 insertions(+), 1 deletions(-)
249 1 files changed, 1 insertions(+), 1 deletions(-)
250
250
251 4 base64-4
251 4 base64-4
252 base64 | 2 +-
252 base64 | 2 +-
253 1 files changed, 1 insertions(+), 1 deletions(-)
253 1 files changed, 1 insertions(+), 1 deletions(-)
254
254
255 3 base64-three
255 3 base64-three
256 base64 | 2 +-
256 base64 | 2 +-
257 1 files changed, 1 insertions(+), 1 deletions(-)
257 1 files changed, 1 insertions(+), 1 deletions(-)
258
258
259 2 base64-two
259 2 base64-two
260 base64 | 2 +-
260 base64 | 2 +-
261 1 files changed, 1 insertions(+), 1 deletions(-)
261 1 files changed, 1 insertions(+), 1 deletions(-)
262
262
263 1 base64-single
263 1 base64-single
264 base64 | 2 +-
264 base64 | 2 +-
265 1 files changed, 1 insertions(+), 1 deletions(-)
265 1 files changed, 1 insertions(+), 1 deletions(-)
266
266
267 0 base64-0
267 0 base64-0
268 base64 | 1 +
268 base64 | 1 +
269 1 files changed, 1 insertions(+), 0 deletions(-)
269 1 files changed, 1 insertions(+), 0 deletions(-)
270
270
271
271
272 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
272 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
273 $ hg -R bundle-again.hg log --stat -T '{rev} {desc}\n' base64
273 $ hg -R bundle-again.hg log --stat -T '{rev} {desc}\n' base64
274 5 branching
274 5 branching
275 base64 | 2 +-
275 base64 | 2 +-
276 1 files changed, 1 insertions(+), 1 deletions(-)
276 1 files changed, 1 insertions(+), 1 deletions(-)
277
277
278 4 base64-4
278 4 base64-4
279 base64 | 2 +-
279 base64 | 2 +-
280 1 files changed, 1 insertions(+), 1 deletions(-)
280 1 files changed, 1 insertions(+), 1 deletions(-)
281
281
282 3 base64-three
282 3 base64-three
283 base64 | 2 +-
283 base64 | 2 +-
284 1 files changed, 1 insertions(+), 1 deletions(-)
284 1 files changed, 1 insertions(+), 1 deletions(-)
285
285
286 2 base64-two
286 2 base64-two
287 base64 | 2 +-
287 base64 | 2 +-
288 1 files changed, 1 insertions(+), 1 deletions(-)
288 1 files changed, 1 insertions(+), 1 deletions(-)
289
289
290 1 base64-single
290 1 base64-single
291 base64 | 2 +-
291 base64 | 2 +-
292 1 files changed, 1 insertions(+), 1 deletions(-)
292 1 files changed, 1 insertions(+), 1 deletions(-)
293
293
294 0 base64-0
294 0 base64-0
295 base64 | 1 +
295 base64 | 1 +
296 1 files changed, 1 insertions(+), 0 deletions(-)
296 1 files changed, 1 insertions(+), 0 deletions(-)
297
297
298 $ rm bundle.hg bundle-again.hg
298 $ rm bundle.hg bundle-again.hg
299 #endif
299 #endif
300
300
301 # TEST: hg status
301 # TEST: hg status
302
302
303 $ hg status
303 $ hg status
304 $ hg diff
304 $ hg diff
General Comments 0
You need to be logged in to leave comments. Login now