##// END OF EJS Templates
py3: catch binascii.Error raised from binascii.unhexlify...
Pulkit Goyal -
r32969:30d0cb27 default
parent child Browse files
Show More
@@ -1,2192 +1,2193 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 binascii
16 import collections
17 import collections
17 import errno
18 import errno
18 import hashlib
19 import hashlib
19 import os
20 import os
20 import struct
21 import struct
21 import zlib
22 import zlib
22
23
23 # import stuff from node for others to import from revlog
24 # import stuff from node for others to import from revlog
24 from .node import (
25 from .node import (
25 bin,
26 bin,
26 hex,
27 hex,
27 nullid,
28 nullid,
28 nullrev,
29 nullrev,
29 wdirhex,
30 wdirhex,
30 wdirid,
31 wdirid,
31 wdirrev,
32 wdirrev,
32 )
33 )
33 from .i18n import _
34 from .i18n import _
34 from . import (
35 from . import (
35 ancestor,
36 ancestor,
36 error,
37 error,
37 mdiff,
38 mdiff,
38 policy,
39 policy,
39 pycompat,
40 pycompat,
40 templatefilters,
41 templatefilters,
41 util,
42 util,
42 )
43 )
43
44
44 parsers = policy.importmod(r'parsers')
45 parsers = policy.importmod(r'parsers')
45
46
46 _pack = struct.pack
47 _pack = struct.pack
47 _unpack = struct.unpack
48 _unpack = struct.unpack
48 # Aliased for performance.
49 # Aliased for performance.
49 _zlibdecompress = zlib.decompress
50 _zlibdecompress = zlib.decompress
50
51
51 # revlog header flags
52 # revlog header flags
52 REVLOGV0 = 0
53 REVLOGV0 = 0
53 REVLOGV1 = 1
54 REVLOGV1 = 1
54 # Dummy value until file format is finalized.
55 # Dummy value until file format is finalized.
55 # Reminder: change the bounds check in revlog.__init__ when this is changed.
56 # Reminder: change the bounds check in revlog.__init__ when this is changed.
56 REVLOGV2 = 0xDEAD
57 REVLOGV2 = 0xDEAD
57 FLAG_INLINE_DATA = (1 << 16)
58 FLAG_INLINE_DATA = (1 << 16)
58 FLAG_GENERALDELTA = (1 << 17)
59 FLAG_GENERALDELTA = (1 << 17)
59 REVLOG_DEFAULT_FLAGS = FLAG_INLINE_DATA
60 REVLOG_DEFAULT_FLAGS = FLAG_INLINE_DATA
60 REVLOG_DEFAULT_FORMAT = REVLOGV1
61 REVLOG_DEFAULT_FORMAT = REVLOGV1
61 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
62 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
62 REVLOGV1_FLAGS = FLAG_INLINE_DATA | FLAG_GENERALDELTA
63 REVLOGV1_FLAGS = FLAG_INLINE_DATA | FLAG_GENERALDELTA
63 REVLOGV2_FLAGS = REVLOGV1_FLAGS
64 REVLOGV2_FLAGS = REVLOGV1_FLAGS
64
65
65 # revlog index flags
66 # revlog index flags
66 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
67 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
67 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
68 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
68 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
69 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
69 REVIDX_DEFAULT_FLAGS = 0
70 REVIDX_DEFAULT_FLAGS = 0
70 # stable order in which flags need to be processed and their processors applied
71 # stable order in which flags need to be processed and their processors applied
71 REVIDX_FLAGS_ORDER = [
72 REVIDX_FLAGS_ORDER = [
72 REVIDX_ISCENSORED,
73 REVIDX_ISCENSORED,
73 REVIDX_ELLIPSIS,
74 REVIDX_ELLIPSIS,
74 REVIDX_EXTSTORED,
75 REVIDX_EXTSTORED,
75 ]
76 ]
76 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
77 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
77
78
78 # max size of revlog with inline data
79 # max size of revlog with inline data
79 _maxinline = 131072
80 _maxinline = 131072
80 _chunksize = 1048576
81 _chunksize = 1048576
81
82
82 RevlogError = error.RevlogError
83 RevlogError = error.RevlogError
83 LookupError = error.LookupError
84 LookupError = error.LookupError
84 CensoredNodeError = error.CensoredNodeError
85 CensoredNodeError = error.CensoredNodeError
85 ProgrammingError = error.ProgrammingError
86 ProgrammingError = error.ProgrammingError
86
87
87 # Store flag processors (cf. 'addflagprocessor()' to register)
88 # Store flag processors (cf. 'addflagprocessor()' to register)
88 _flagprocessors = {
89 _flagprocessors = {
89 REVIDX_ISCENSORED: None,
90 REVIDX_ISCENSORED: None,
90 }
91 }
91
92
92 def addflagprocessor(flag, processor):
93 def addflagprocessor(flag, processor):
93 """Register a flag processor on a revision data flag.
94 """Register a flag processor on a revision data flag.
94
95
95 Invariant:
96 Invariant:
96 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER.
97 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER.
97 - Only one flag processor can be registered on a specific flag.
98 - Only one flag processor can be registered on a specific flag.
98 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
99 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
99 following signatures:
100 following signatures:
100 - (read) f(self, rawtext) -> text, bool
101 - (read) f(self, rawtext) -> text, bool
101 - (write) f(self, text) -> rawtext, bool
102 - (write) f(self, text) -> rawtext, bool
102 - (raw) f(self, rawtext) -> bool
103 - (raw) f(self, rawtext) -> bool
103 "text" is presented to the user. "rawtext" is stored in revlog data, not
104 "text" is presented to the user. "rawtext" is stored in revlog data, not
104 directly visible to the user.
105 directly visible to the user.
105 The boolean returned by these transforms is used to determine whether
106 The boolean returned by these transforms is used to determine whether
106 the returned text can be used for hash integrity checking. For example,
107 the returned text can be used for hash integrity checking. For example,
107 if "write" returns False, then "text" is used to generate hash. If
108 if "write" returns False, then "text" is used to generate hash. If
108 "write" returns True, that basically means "rawtext" returned by "write"
109 "write" returns True, that basically means "rawtext" returned by "write"
109 should be used to generate hash. Usually, "write" and "read" return
110 should be used to generate hash. Usually, "write" and "read" return
110 different booleans. And "raw" returns a same boolean as "write".
111 different booleans. And "raw" returns a same boolean as "write".
111
112
112 Note: The 'raw' transform is used for changegroup generation and in some
113 Note: The 'raw' transform is used for changegroup generation and in some
113 debug commands. In this case the transform only indicates whether the
114 debug commands. In this case the transform only indicates whether the
114 contents can be used for hash integrity checks.
115 contents can be used for hash integrity checks.
115 """
116 """
116 if not flag & REVIDX_KNOWN_FLAGS:
117 if not flag & REVIDX_KNOWN_FLAGS:
117 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
118 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
118 raise ProgrammingError(msg)
119 raise ProgrammingError(msg)
119 if flag not in REVIDX_FLAGS_ORDER:
120 if flag not in REVIDX_FLAGS_ORDER:
120 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
121 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
121 raise ProgrammingError(msg)
122 raise ProgrammingError(msg)
122 if flag in _flagprocessors:
123 if flag in _flagprocessors:
123 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
124 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
124 raise error.Abort(msg)
125 raise error.Abort(msg)
125 _flagprocessors[flag] = processor
126 _flagprocessors[flag] = processor
126
127
127 def getoffset(q):
128 def getoffset(q):
128 return int(q >> 16)
129 return int(q >> 16)
129
130
130 def gettype(q):
131 def gettype(q):
131 return int(q & 0xFFFF)
132 return int(q & 0xFFFF)
132
133
133 def offset_type(offset, type):
134 def offset_type(offset, type):
134 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
135 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
135 raise ValueError('unknown revlog index flags')
136 raise ValueError('unknown revlog index flags')
136 return int(int(offset) << 16 | type)
137 return int(int(offset) << 16 | type)
137
138
138 _nullhash = hashlib.sha1(nullid)
139 _nullhash = hashlib.sha1(nullid)
139
140
140 def hash(text, p1, p2):
141 def hash(text, p1, p2):
141 """generate a hash from the given text and its parent hashes
142 """generate a hash from the given text and its parent hashes
142
143
143 This hash combines both the current file contents and its history
144 This hash combines both the current file contents and its history
144 in a manner that makes it easy to distinguish nodes with the same
145 in a manner that makes it easy to distinguish nodes with the same
145 content in the revision graph.
146 content in the revision graph.
146 """
147 """
147 # As of now, if one of the parent node is null, p2 is null
148 # As of now, if one of the parent node is null, p2 is null
148 if p2 == nullid:
149 if p2 == nullid:
149 # deep copy of a hash is faster than creating one
150 # deep copy of a hash is faster than creating one
150 s = _nullhash.copy()
151 s = _nullhash.copy()
151 s.update(p1)
152 s.update(p1)
152 else:
153 else:
153 # none of the parent nodes are nullid
154 # none of the parent nodes are nullid
154 l = [p1, p2]
155 l = [p1, p2]
155 l.sort()
156 l.sort()
156 s = hashlib.sha1(l[0])
157 s = hashlib.sha1(l[0])
157 s.update(l[1])
158 s.update(l[1])
158 s.update(text)
159 s.update(text)
159 return s.digest()
160 return s.digest()
160
161
161 # index v0:
162 # index v0:
162 # 4 bytes: offset
163 # 4 bytes: offset
163 # 4 bytes: compressed length
164 # 4 bytes: compressed length
164 # 4 bytes: base rev
165 # 4 bytes: base rev
165 # 4 bytes: link rev
166 # 4 bytes: link rev
166 # 20 bytes: parent 1 nodeid
167 # 20 bytes: parent 1 nodeid
167 # 20 bytes: parent 2 nodeid
168 # 20 bytes: parent 2 nodeid
168 # 20 bytes: nodeid
169 # 20 bytes: nodeid
169 indexformatv0 = ">4l20s20s20s"
170 indexformatv0 = ">4l20s20s20s"
170
171
171 class revlogoldio(object):
172 class revlogoldio(object):
172 def __init__(self):
173 def __init__(self):
173 self.size = struct.calcsize(indexformatv0)
174 self.size = struct.calcsize(indexformatv0)
174
175
175 def parseindex(self, data, inline):
176 def parseindex(self, data, inline):
176 s = self.size
177 s = self.size
177 index = []
178 index = []
178 nodemap = {nullid: nullrev}
179 nodemap = {nullid: nullrev}
179 n = off = 0
180 n = off = 0
180 l = len(data)
181 l = len(data)
181 while off + s <= l:
182 while off + s <= l:
182 cur = data[off:off + s]
183 cur = data[off:off + s]
183 off += s
184 off += s
184 e = _unpack(indexformatv0, cur)
185 e = _unpack(indexformatv0, cur)
185 # transform to revlogv1 format
186 # transform to revlogv1 format
186 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
187 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
187 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
188 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
188 index.append(e2)
189 index.append(e2)
189 nodemap[e[6]] = n
190 nodemap[e[6]] = n
190 n += 1
191 n += 1
191
192
192 # add the magic null revision at -1
193 # add the magic null revision at -1
193 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
194 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
194
195
195 return index, nodemap, None
196 return index, nodemap, None
196
197
197 def packentry(self, entry, node, version, rev):
198 def packentry(self, entry, node, version, rev):
198 if gettype(entry[0]):
199 if gettype(entry[0]):
199 raise RevlogError(_('index entry flags need revlog version 1'))
200 raise RevlogError(_('index entry flags need revlog version 1'))
200 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
201 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
201 node(entry[5]), node(entry[6]), entry[7])
202 node(entry[5]), node(entry[6]), entry[7])
202 return _pack(indexformatv0, *e2)
203 return _pack(indexformatv0, *e2)
203
204
204 # index ng:
205 # index ng:
205 # 6 bytes: offset
206 # 6 bytes: offset
206 # 2 bytes: flags
207 # 2 bytes: flags
207 # 4 bytes: compressed length
208 # 4 bytes: compressed length
208 # 4 bytes: uncompressed length
209 # 4 bytes: uncompressed length
209 # 4 bytes: base rev
210 # 4 bytes: base rev
210 # 4 bytes: link rev
211 # 4 bytes: link rev
211 # 4 bytes: parent 1 rev
212 # 4 bytes: parent 1 rev
212 # 4 bytes: parent 2 rev
213 # 4 bytes: parent 2 rev
213 # 32 bytes: nodeid
214 # 32 bytes: nodeid
214 indexformatng = ">Qiiiiii20s12x"
215 indexformatng = ">Qiiiiii20s12x"
215 versionformat = ">I"
216 versionformat = ">I"
216
217
217 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
218 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
218 # signed integer)
219 # signed integer)
219 _maxentrysize = 0x7fffffff
220 _maxentrysize = 0x7fffffff
220
221
221 class revlogio(object):
222 class revlogio(object):
222 def __init__(self):
223 def __init__(self):
223 self.size = struct.calcsize(indexformatng)
224 self.size = struct.calcsize(indexformatng)
224
225
225 def parseindex(self, data, inline):
226 def parseindex(self, data, inline):
226 # call the C implementation to parse the index data
227 # call the C implementation to parse the index data
227 index, cache = parsers.parse_index2(data, inline)
228 index, cache = parsers.parse_index2(data, inline)
228 return index, getattr(index, 'nodemap', None), cache
229 return index, getattr(index, 'nodemap', None), cache
229
230
230 def packentry(self, entry, node, version, rev):
231 def packentry(self, entry, node, version, rev):
231 p = _pack(indexformatng, *entry)
232 p = _pack(indexformatng, *entry)
232 if rev == 0:
233 if rev == 0:
233 p = _pack(versionformat, version) + p[4:]
234 p = _pack(versionformat, version) + p[4:]
234 return p
235 return p
235
236
236 class revlog(object):
237 class revlog(object):
237 """
238 """
238 the underlying revision storage object
239 the underlying revision storage object
239
240
240 A revlog consists of two parts, an index and the revision data.
241 A revlog consists of two parts, an index and the revision data.
241
242
242 The index is a file with a fixed record size containing
243 The index is a file with a fixed record size containing
243 information on each revision, including its nodeid (hash), the
244 information on each revision, including its nodeid (hash), the
244 nodeids of its parents, the position and offset of its data within
245 nodeids of its parents, the position and offset of its data within
245 the data file, and the revision it's based on. Finally, each entry
246 the data file, and the revision it's based on. Finally, each entry
246 contains a linkrev entry that can serve as a pointer to external
247 contains a linkrev entry that can serve as a pointer to external
247 data.
248 data.
248
249
249 The revision data itself is a linear collection of data chunks.
250 The revision data itself is a linear collection of data chunks.
250 Each chunk represents a revision and is usually represented as a
251 Each chunk represents a revision and is usually represented as a
251 delta against the previous chunk. To bound lookup time, runs of
252 delta against the previous chunk. To bound lookup time, runs of
252 deltas are limited to about 2 times the length of the original
253 deltas are limited to about 2 times the length of the original
253 version data. This makes retrieval of a version proportional to
254 version data. This makes retrieval of a version proportional to
254 its size, or O(1) relative to the number of revisions.
255 its size, or O(1) relative to the number of revisions.
255
256
256 Both pieces of the revlog are written to in an append-only
257 Both pieces of the revlog are written to in an append-only
257 fashion, which means we never need to rewrite a file to insert or
258 fashion, which means we never need to rewrite a file to insert or
258 remove data, and can use some simple techniques to avoid the need
259 remove data, and can use some simple techniques to avoid the need
259 for locking while reading.
260 for locking while reading.
260
261
261 If checkambig, indexfile is opened with checkambig=True at
262 If checkambig, indexfile is opened with checkambig=True at
262 writing, to avoid file stat ambiguity.
263 writing, to avoid file stat ambiguity.
263 """
264 """
264 def __init__(self, opener, indexfile, datafile=None, checkambig=False):
265 def __init__(self, opener, indexfile, datafile=None, checkambig=False):
265 """
266 """
266 create a revlog object
267 create a revlog object
267
268
268 opener is a function that abstracts the file opening operation
269 opener is a function that abstracts the file opening operation
269 and can be used to implement COW semantics or the like.
270 and can be used to implement COW semantics or the like.
270 """
271 """
271 self.indexfile = indexfile
272 self.indexfile = indexfile
272 self.datafile = datafile or (indexfile[:-2] + ".d")
273 self.datafile = datafile or (indexfile[:-2] + ".d")
273 self.opener = opener
274 self.opener = opener
274 # When True, indexfile is opened with checkambig=True at writing, to
275 # When True, indexfile is opened with checkambig=True at writing, to
275 # avoid file stat ambiguity.
276 # avoid file stat ambiguity.
276 self._checkambig = checkambig
277 self._checkambig = checkambig
277 # 3-tuple of (node, rev, text) for a raw revision.
278 # 3-tuple of (node, rev, text) for a raw revision.
278 self._cache = None
279 self._cache = None
279 # Maps rev to chain base rev.
280 # Maps rev to chain base rev.
280 self._chainbasecache = util.lrucachedict(100)
281 self._chainbasecache = util.lrucachedict(100)
281 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
282 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
282 self._chunkcache = (0, '')
283 self._chunkcache = (0, '')
283 # How much data to read and cache into the raw revlog data cache.
284 # How much data to read and cache into the raw revlog data cache.
284 self._chunkcachesize = 65536
285 self._chunkcachesize = 65536
285 self._maxchainlen = None
286 self._maxchainlen = None
286 self._aggressivemergedeltas = False
287 self._aggressivemergedeltas = False
287 self.index = []
288 self.index = []
288 # Mapping of partial identifiers to full nodes.
289 # Mapping of partial identifiers to full nodes.
289 self._pcache = {}
290 self._pcache = {}
290 # Mapping of revision integer to full node.
291 # Mapping of revision integer to full node.
291 self._nodecache = {nullid: nullrev}
292 self._nodecache = {nullid: nullrev}
292 self._nodepos = None
293 self._nodepos = None
293 self._compengine = 'zlib'
294 self._compengine = 'zlib'
294
295
295 v = REVLOG_DEFAULT_VERSION
296 v = REVLOG_DEFAULT_VERSION
296 opts = getattr(opener, 'options', None)
297 opts = getattr(opener, 'options', None)
297 if opts is not None:
298 if opts is not None:
298 if 'revlogv2' in opts:
299 if 'revlogv2' in opts:
299 # version 2 revlogs always use generaldelta.
300 # version 2 revlogs always use generaldelta.
300 v = REVLOGV2 | FLAG_GENERALDELTA | FLAG_INLINE_DATA
301 v = REVLOGV2 | FLAG_GENERALDELTA | FLAG_INLINE_DATA
301 elif 'revlogv1' in opts:
302 elif 'revlogv1' in opts:
302 if 'generaldelta' in opts:
303 if 'generaldelta' in opts:
303 v |= FLAG_GENERALDELTA
304 v |= FLAG_GENERALDELTA
304 else:
305 else:
305 v = 0
306 v = 0
306 if 'chunkcachesize' in opts:
307 if 'chunkcachesize' in opts:
307 self._chunkcachesize = opts['chunkcachesize']
308 self._chunkcachesize = opts['chunkcachesize']
308 if 'maxchainlen' in opts:
309 if 'maxchainlen' in opts:
309 self._maxchainlen = opts['maxchainlen']
310 self._maxchainlen = opts['maxchainlen']
310 if 'aggressivemergedeltas' in opts:
311 if 'aggressivemergedeltas' in opts:
311 self._aggressivemergedeltas = opts['aggressivemergedeltas']
312 self._aggressivemergedeltas = opts['aggressivemergedeltas']
312 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
313 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
313 if 'compengine' in opts:
314 if 'compengine' in opts:
314 self._compengine = opts['compengine']
315 self._compengine = opts['compengine']
315
316
316 if self._chunkcachesize <= 0:
317 if self._chunkcachesize <= 0:
317 raise RevlogError(_('revlog chunk cache size %r is not greater '
318 raise RevlogError(_('revlog chunk cache size %r is not greater '
318 'than 0') % self._chunkcachesize)
319 'than 0') % self._chunkcachesize)
319 elif self._chunkcachesize & (self._chunkcachesize - 1):
320 elif self._chunkcachesize & (self._chunkcachesize - 1):
320 raise RevlogError(_('revlog chunk cache size %r is not a power '
321 raise RevlogError(_('revlog chunk cache size %r is not a power '
321 'of 2') % self._chunkcachesize)
322 'of 2') % self._chunkcachesize)
322
323
323 indexdata = ''
324 indexdata = ''
324 self._initempty = True
325 self._initempty = True
325 try:
326 try:
326 f = self.opener(self.indexfile)
327 f = self.opener(self.indexfile)
327 indexdata = f.read()
328 indexdata = f.read()
328 f.close()
329 f.close()
329 if len(indexdata) > 0:
330 if len(indexdata) > 0:
330 v = struct.unpack(versionformat, indexdata[:4])[0]
331 v = struct.unpack(versionformat, indexdata[:4])[0]
331 self._initempty = False
332 self._initempty = False
332 except IOError as inst:
333 except IOError as inst:
333 if inst.errno != errno.ENOENT:
334 if inst.errno != errno.ENOENT:
334 raise
335 raise
335
336
336 self.version = v
337 self.version = v
337 self._inline = v & FLAG_INLINE_DATA
338 self._inline = v & FLAG_INLINE_DATA
338 self._generaldelta = v & FLAG_GENERALDELTA
339 self._generaldelta = v & FLAG_GENERALDELTA
339 flags = v & ~0xFFFF
340 flags = v & ~0xFFFF
340 fmt = v & 0xFFFF
341 fmt = v & 0xFFFF
341 if fmt == REVLOGV0:
342 if fmt == REVLOGV0:
342 if flags:
343 if flags:
343 raise RevlogError(_('unknown flags (%#04x) in version %d '
344 raise RevlogError(_('unknown flags (%#04x) in version %d '
344 'revlog %s') %
345 'revlog %s') %
345 (flags >> 16, fmt, self.indexfile))
346 (flags >> 16, fmt, self.indexfile))
346 elif fmt == REVLOGV1:
347 elif fmt == REVLOGV1:
347 if flags & ~REVLOGV1_FLAGS:
348 if flags & ~REVLOGV1_FLAGS:
348 raise RevlogError(_('unknown flags (%#04x) in version %d '
349 raise RevlogError(_('unknown flags (%#04x) in version %d '
349 'revlog %s') %
350 'revlog %s') %
350 (flags >> 16, fmt, self.indexfile))
351 (flags >> 16, fmt, self.indexfile))
351 elif fmt == REVLOGV2:
352 elif fmt == REVLOGV2:
352 if flags & ~REVLOGV2_FLAGS:
353 if flags & ~REVLOGV2_FLAGS:
353 raise RevlogError(_('unknown flags (%#04x) in version %d '
354 raise RevlogError(_('unknown flags (%#04x) in version %d '
354 'revlog %s') %
355 'revlog %s') %
355 (flags >> 16, fmt, self.indexfile))
356 (flags >> 16, fmt, self.indexfile))
356 else:
357 else:
357 raise RevlogError(_('unknown version (%d) in revlog %s') %
358 raise RevlogError(_('unknown version (%d) in revlog %s') %
358 (fmt, self.indexfile))
359 (fmt, self.indexfile))
359
360
360 self.storedeltachains = True
361 self.storedeltachains = True
361
362
362 self._io = revlogio()
363 self._io = revlogio()
363 if self.version == REVLOGV0:
364 if self.version == REVLOGV0:
364 self._io = revlogoldio()
365 self._io = revlogoldio()
365 try:
366 try:
366 d = self._io.parseindex(indexdata, self._inline)
367 d = self._io.parseindex(indexdata, self._inline)
367 except (ValueError, IndexError):
368 except (ValueError, IndexError):
368 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
369 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
369 self.index, nodemap, self._chunkcache = d
370 self.index, nodemap, self._chunkcache = d
370 if nodemap is not None:
371 if nodemap is not None:
371 self.nodemap = self._nodecache = nodemap
372 self.nodemap = self._nodecache = nodemap
372 if not self._chunkcache:
373 if not self._chunkcache:
373 self._chunkclear()
374 self._chunkclear()
374 # revnum -> (chain-length, sum-delta-length)
375 # revnum -> (chain-length, sum-delta-length)
375 self._chaininfocache = {}
376 self._chaininfocache = {}
376 # revlog header -> revlog compressor
377 # revlog header -> revlog compressor
377 self._decompressors = {}
378 self._decompressors = {}
378
379
379 @util.propertycache
380 @util.propertycache
380 def _compressor(self):
381 def _compressor(self):
381 return util.compengines[self._compengine].revlogcompressor()
382 return util.compengines[self._compengine].revlogcompressor()
382
383
383 def tip(self):
384 def tip(self):
384 return self.node(len(self.index) - 2)
385 return self.node(len(self.index) - 2)
385 def __contains__(self, rev):
386 def __contains__(self, rev):
386 return 0 <= rev < len(self)
387 return 0 <= rev < len(self)
387 def __len__(self):
388 def __len__(self):
388 return len(self.index) - 1
389 return len(self.index) - 1
389 def __iter__(self):
390 def __iter__(self):
390 return iter(xrange(len(self)))
391 return iter(xrange(len(self)))
391 def revs(self, start=0, stop=None):
392 def revs(self, start=0, stop=None):
392 """iterate over all rev in this revlog (from start to stop)"""
393 """iterate over all rev in this revlog (from start to stop)"""
393 step = 1
394 step = 1
394 if stop is not None:
395 if stop is not None:
395 if start > stop:
396 if start > stop:
396 step = -1
397 step = -1
397 stop += step
398 stop += step
398 else:
399 else:
399 stop = len(self)
400 stop = len(self)
400 return xrange(start, stop, step)
401 return xrange(start, stop, step)
401
402
402 @util.propertycache
403 @util.propertycache
403 def nodemap(self):
404 def nodemap(self):
404 self.rev(self.node(0))
405 self.rev(self.node(0))
405 return self._nodecache
406 return self._nodecache
406
407
407 def hasnode(self, node):
408 def hasnode(self, node):
408 try:
409 try:
409 self.rev(node)
410 self.rev(node)
410 return True
411 return True
411 except KeyError:
412 except KeyError:
412 return False
413 return False
413
414
414 def clearcaches(self):
415 def clearcaches(self):
415 self._cache = None
416 self._cache = None
416 self._chainbasecache.clear()
417 self._chainbasecache.clear()
417 self._chunkcache = (0, '')
418 self._chunkcache = (0, '')
418 self._pcache = {}
419 self._pcache = {}
419
420
420 try:
421 try:
421 self._nodecache.clearcaches()
422 self._nodecache.clearcaches()
422 except AttributeError:
423 except AttributeError:
423 self._nodecache = {nullid: nullrev}
424 self._nodecache = {nullid: nullrev}
424 self._nodepos = None
425 self._nodepos = None
425
426
426 def rev(self, node):
427 def rev(self, node):
427 try:
428 try:
428 return self._nodecache[node]
429 return self._nodecache[node]
429 except TypeError:
430 except TypeError:
430 raise
431 raise
431 except RevlogError:
432 except RevlogError:
432 # parsers.c radix tree lookup failed
433 # parsers.c radix tree lookup failed
433 if node == wdirid:
434 if node == wdirid:
434 raise error.WdirUnsupported
435 raise error.WdirUnsupported
435 raise LookupError(node, self.indexfile, _('no node'))
436 raise LookupError(node, self.indexfile, _('no node'))
436 except KeyError:
437 except KeyError:
437 # pure python cache lookup failed
438 # pure python cache lookup failed
438 n = self._nodecache
439 n = self._nodecache
439 i = self.index
440 i = self.index
440 p = self._nodepos
441 p = self._nodepos
441 if p is None:
442 if p is None:
442 p = len(i) - 2
443 p = len(i) - 2
443 for r in xrange(p, -1, -1):
444 for r in xrange(p, -1, -1):
444 v = i[r][7]
445 v = i[r][7]
445 n[v] = r
446 n[v] = r
446 if v == node:
447 if v == node:
447 self._nodepos = r - 1
448 self._nodepos = r - 1
448 return r
449 return r
449 if node == wdirid:
450 if node == wdirid:
450 raise error.WdirUnsupported
451 raise error.WdirUnsupported
451 raise LookupError(node, self.indexfile, _('no node'))
452 raise LookupError(node, self.indexfile, _('no node'))
452
453
453 # Accessors for index entries.
454 # Accessors for index entries.
454
455
455 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
456 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
456 # are flags.
457 # are flags.
457 def start(self, rev):
458 def start(self, rev):
458 return int(self.index[rev][0] >> 16)
459 return int(self.index[rev][0] >> 16)
459
460
460 def flags(self, rev):
461 def flags(self, rev):
461 return self.index[rev][0] & 0xFFFF
462 return self.index[rev][0] & 0xFFFF
462
463
463 def length(self, rev):
464 def length(self, rev):
464 return self.index[rev][1]
465 return self.index[rev][1]
465
466
466 def rawsize(self, rev):
467 def rawsize(self, rev):
467 """return the length of the uncompressed text for a given revision"""
468 """return the length of the uncompressed text for a given revision"""
468 l = self.index[rev][2]
469 l = self.index[rev][2]
469 if l >= 0:
470 if l >= 0:
470 return l
471 return l
471
472
472 t = self.revision(rev, raw=True)
473 t = self.revision(rev, raw=True)
473 return len(t)
474 return len(t)
474
475
475 def size(self, rev):
476 def size(self, rev):
476 """length of non-raw text (processed by a "read" flag processor)"""
477 """length of non-raw text (processed by a "read" flag processor)"""
477 # fast path: if no "read" flag processor could change the content,
478 # fast path: if no "read" flag processor could change the content,
478 # size is rawsize. note: ELLIPSIS is known to not change the content.
479 # size is rawsize. note: ELLIPSIS is known to not change the content.
479 flags = self.flags(rev)
480 flags = self.flags(rev)
480 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
481 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
481 return self.rawsize(rev)
482 return self.rawsize(rev)
482
483
483 return len(self.revision(rev, raw=False))
484 return len(self.revision(rev, raw=False))
484
485
485 def chainbase(self, rev):
486 def chainbase(self, rev):
486 base = self._chainbasecache.get(rev)
487 base = self._chainbasecache.get(rev)
487 if base is not None:
488 if base is not None:
488 return base
489 return base
489
490
490 index = self.index
491 index = self.index
491 base = index[rev][3]
492 base = index[rev][3]
492 while base != rev:
493 while base != rev:
493 rev = base
494 rev = base
494 base = index[rev][3]
495 base = index[rev][3]
495
496
496 self._chainbasecache[rev] = base
497 self._chainbasecache[rev] = base
497 return base
498 return base
498
499
499 def linkrev(self, rev):
500 def linkrev(self, rev):
500 return self.index[rev][4]
501 return self.index[rev][4]
501
502
502 def parentrevs(self, rev):
503 def parentrevs(self, rev):
503 try:
504 try:
504 return self.index[rev][5:7]
505 return self.index[rev][5:7]
505 except IndexError:
506 except IndexError:
506 if rev == wdirrev:
507 if rev == wdirrev:
507 raise error.WdirUnsupported
508 raise error.WdirUnsupported
508 raise
509 raise
509
510
510 def node(self, rev):
511 def node(self, rev):
511 try:
512 try:
512 return self.index[rev][7]
513 return self.index[rev][7]
513 except IndexError:
514 except IndexError:
514 if rev == wdirrev:
515 if rev == wdirrev:
515 raise error.WdirUnsupported
516 raise error.WdirUnsupported
516 raise
517 raise
517
518
518 # Derived from index values.
519 # Derived from index values.
519
520
520 def end(self, rev):
521 def end(self, rev):
521 return self.start(rev) + self.length(rev)
522 return self.start(rev) + self.length(rev)
522
523
523 def parents(self, node):
524 def parents(self, node):
524 i = self.index
525 i = self.index
525 d = i[self.rev(node)]
526 d = i[self.rev(node)]
526 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
527 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
527
528
528 def chainlen(self, rev):
529 def chainlen(self, rev):
529 return self._chaininfo(rev)[0]
530 return self._chaininfo(rev)[0]
530
531
531 def _chaininfo(self, rev):
532 def _chaininfo(self, rev):
532 chaininfocache = self._chaininfocache
533 chaininfocache = self._chaininfocache
533 if rev in chaininfocache:
534 if rev in chaininfocache:
534 return chaininfocache[rev]
535 return chaininfocache[rev]
535 index = self.index
536 index = self.index
536 generaldelta = self._generaldelta
537 generaldelta = self._generaldelta
537 iterrev = rev
538 iterrev = rev
538 e = index[iterrev]
539 e = index[iterrev]
539 clen = 0
540 clen = 0
540 compresseddeltalen = 0
541 compresseddeltalen = 0
541 while iterrev != e[3]:
542 while iterrev != e[3]:
542 clen += 1
543 clen += 1
543 compresseddeltalen += e[1]
544 compresseddeltalen += e[1]
544 if generaldelta:
545 if generaldelta:
545 iterrev = e[3]
546 iterrev = e[3]
546 else:
547 else:
547 iterrev -= 1
548 iterrev -= 1
548 if iterrev in chaininfocache:
549 if iterrev in chaininfocache:
549 t = chaininfocache[iterrev]
550 t = chaininfocache[iterrev]
550 clen += t[0]
551 clen += t[0]
551 compresseddeltalen += t[1]
552 compresseddeltalen += t[1]
552 break
553 break
553 e = index[iterrev]
554 e = index[iterrev]
554 else:
555 else:
555 # Add text length of base since decompressing that also takes
556 # Add text length of base since decompressing that also takes
556 # work. For cache hits the length is already included.
557 # work. For cache hits the length is already included.
557 compresseddeltalen += e[1]
558 compresseddeltalen += e[1]
558 r = (clen, compresseddeltalen)
559 r = (clen, compresseddeltalen)
559 chaininfocache[rev] = r
560 chaininfocache[rev] = r
560 return r
561 return r
561
562
562 def _deltachain(self, rev, stoprev=None):
563 def _deltachain(self, rev, stoprev=None):
563 """Obtain the delta chain for a revision.
564 """Obtain the delta chain for a revision.
564
565
565 ``stoprev`` specifies a revision to stop at. If not specified, we
566 ``stoprev`` specifies a revision to stop at. If not specified, we
566 stop at the base of the chain.
567 stop at the base of the chain.
567
568
568 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
569 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
569 revs in ascending order and ``stopped`` is a bool indicating whether
570 revs in ascending order and ``stopped`` is a bool indicating whether
570 ``stoprev`` was hit.
571 ``stoprev`` was hit.
571 """
572 """
572 chain = []
573 chain = []
573
574
574 # Alias to prevent attribute lookup in tight loop.
575 # Alias to prevent attribute lookup in tight loop.
575 index = self.index
576 index = self.index
576 generaldelta = self._generaldelta
577 generaldelta = self._generaldelta
577
578
578 iterrev = rev
579 iterrev = rev
579 e = index[iterrev]
580 e = index[iterrev]
580 while iterrev != e[3] and iterrev != stoprev:
581 while iterrev != e[3] and iterrev != stoprev:
581 chain.append(iterrev)
582 chain.append(iterrev)
582 if generaldelta:
583 if generaldelta:
583 iterrev = e[3]
584 iterrev = e[3]
584 else:
585 else:
585 iterrev -= 1
586 iterrev -= 1
586 e = index[iterrev]
587 e = index[iterrev]
587
588
588 if iterrev == stoprev:
589 if iterrev == stoprev:
589 stopped = True
590 stopped = True
590 else:
591 else:
591 chain.append(iterrev)
592 chain.append(iterrev)
592 stopped = False
593 stopped = False
593
594
594 chain.reverse()
595 chain.reverse()
595 return chain, stopped
596 return chain, stopped
596
597
597 def ancestors(self, revs, stoprev=0, inclusive=False):
598 def ancestors(self, revs, stoprev=0, inclusive=False):
598 """Generate the ancestors of 'revs' in reverse topological order.
599 """Generate the ancestors of 'revs' in reverse topological order.
599 Does not generate revs lower than stoprev.
600 Does not generate revs lower than stoprev.
600
601
601 See the documentation for ancestor.lazyancestors for more details."""
602 See the documentation for ancestor.lazyancestors for more details."""
602
603
603 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
604 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
604 inclusive=inclusive)
605 inclusive=inclusive)
605
606
606 def descendants(self, revs):
607 def descendants(self, revs):
607 """Generate the descendants of 'revs' in revision order.
608 """Generate the descendants of 'revs' in revision order.
608
609
609 Yield a sequence of revision numbers starting with a child of
610 Yield a sequence of revision numbers starting with a child of
610 some rev in revs, i.e., each revision is *not* considered a
611 some rev in revs, i.e., each revision is *not* considered a
611 descendant of itself. Results are ordered by revision number (a
612 descendant of itself. Results are ordered by revision number (a
612 topological sort)."""
613 topological sort)."""
613 first = min(revs)
614 first = min(revs)
614 if first == nullrev:
615 if first == nullrev:
615 for i in self:
616 for i in self:
616 yield i
617 yield i
617 return
618 return
618
619
619 seen = set(revs)
620 seen = set(revs)
620 for i in self.revs(start=first + 1):
621 for i in self.revs(start=first + 1):
621 for x in self.parentrevs(i):
622 for x in self.parentrevs(i):
622 if x != nullrev and x in seen:
623 if x != nullrev and x in seen:
623 seen.add(i)
624 seen.add(i)
624 yield i
625 yield i
625 break
626 break
626
627
627 def findcommonmissing(self, common=None, heads=None):
628 def findcommonmissing(self, common=None, heads=None):
628 """Return a tuple of the ancestors of common and the ancestors of heads
629 """Return a tuple of the ancestors of common and the ancestors of heads
629 that are not ancestors of common. In revset terminology, we return the
630 that are not ancestors of common. In revset terminology, we return the
630 tuple:
631 tuple:
631
632
632 ::common, (::heads) - (::common)
633 ::common, (::heads) - (::common)
633
634
634 The list is sorted by revision number, meaning it is
635 The list is sorted by revision number, meaning it is
635 topologically sorted.
636 topologically sorted.
636
637
637 'heads' and 'common' are both lists of node IDs. If heads is
638 'heads' and 'common' are both lists of node IDs. If heads is
638 not supplied, uses all of the revlog's heads. If common is not
639 not supplied, uses all of the revlog's heads. If common is not
639 supplied, uses nullid."""
640 supplied, uses nullid."""
640 if common is None:
641 if common is None:
641 common = [nullid]
642 common = [nullid]
642 if heads is None:
643 if heads is None:
643 heads = self.heads()
644 heads = self.heads()
644
645
645 common = [self.rev(n) for n in common]
646 common = [self.rev(n) for n in common]
646 heads = [self.rev(n) for n in heads]
647 heads = [self.rev(n) for n in heads]
647
648
648 # we want the ancestors, but inclusive
649 # we want the ancestors, but inclusive
649 class lazyset(object):
650 class lazyset(object):
650 def __init__(self, lazyvalues):
651 def __init__(self, lazyvalues):
651 self.addedvalues = set()
652 self.addedvalues = set()
652 self.lazyvalues = lazyvalues
653 self.lazyvalues = lazyvalues
653
654
654 def __contains__(self, value):
655 def __contains__(self, value):
655 return value in self.addedvalues or value in self.lazyvalues
656 return value in self.addedvalues or value in self.lazyvalues
656
657
657 def __iter__(self):
658 def __iter__(self):
658 added = self.addedvalues
659 added = self.addedvalues
659 for r in added:
660 for r in added:
660 yield r
661 yield r
661 for r in self.lazyvalues:
662 for r in self.lazyvalues:
662 if not r in added:
663 if not r in added:
663 yield r
664 yield r
664
665
665 def add(self, value):
666 def add(self, value):
666 self.addedvalues.add(value)
667 self.addedvalues.add(value)
667
668
668 def update(self, values):
669 def update(self, values):
669 self.addedvalues.update(values)
670 self.addedvalues.update(values)
670
671
671 has = lazyset(self.ancestors(common))
672 has = lazyset(self.ancestors(common))
672 has.add(nullrev)
673 has.add(nullrev)
673 has.update(common)
674 has.update(common)
674
675
675 # take all ancestors from heads that aren't in has
676 # take all ancestors from heads that aren't in has
676 missing = set()
677 missing = set()
677 visit = collections.deque(r for r in heads if r not in has)
678 visit = collections.deque(r for r in heads if r not in has)
678 while visit:
679 while visit:
679 r = visit.popleft()
680 r = visit.popleft()
680 if r in missing:
681 if r in missing:
681 continue
682 continue
682 else:
683 else:
683 missing.add(r)
684 missing.add(r)
684 for p in self.parentrevs(r):
685 for p in self.parentrevs(r):
685 if p not in has:
686 if p not in has:
686 visit.append(p)
687 visit.append(p)
687 missing = list(missing)
688 missing = list(missing)
688 missing.sort()
689 missing.sort()
689 return has, [self.node(miss) for miss in missing]
690 return has, [self.node(miss) for miss in missing]
690
691
691 def incrementalmissingrevs(self, common=None):
692 def incrementalmissingrevs(self, common=None):
692 """Return an object that can be used to incrementally compute the
693 """Return an object that can be used to incrementally compute the
693 revision numbers of the ancestors of arbitrary sets that are not
694 revision numbers of the ancestors of arbitrary sets that are not
694 ancestors of common. This is an ancestor.incrementalmissingancestors
695 ancestors of common. This is an ancestor.incrementalmissingancestors
695 object.
696 object.
696
697
697 'common' is a list of revision numbers. If common is not supplied, uses
698 'common' is a list of revision numbers. If common is not supplied, uses
698 nullrev.
699 nullrev.
699 """
700 """
700 if common is None:
701 if common is None:
701 common = [nullrev]
702 common = [nullrev]
702
703
703 return ancestor.incrementalmissingancestors(self.parentrevs, common)
704 return ancestor.incrementalmissingancestors(self.parentrevs, common)
704
705
705 def findmissingrevs(self, common=None, heads=None):
706 def findmissingrevs(self, common=None, heads=None):
706 """Return the revision numbers of the ancestors of heads that
707 """Return the revision numbers of the ancestors of heads that
707 are not ancestors of common.
708 are not ancestors of common.
708
709
709 More specifically, return a list of revision numbers corresponding to
710 More specifically, return a list of revision numbers corresponding to
710 nodes N such that every N satisfies the following constraints:
711 nodes N such that every N satisfies the following constraints:
711
712
712 1. N is an ancestor of some node in 'heads'
713 1. N is an ancestor of some node in 'heads'
713 2. N is not an ancestor of any node in 'common'
714 2. N is not an ancestor of any node in 'common'
714
715
715 The list is sorted by revision number, meaning it is
716 The list is sorted by revision number, meaning it is
716 topologically sorted.
717 topologically sorted.
717
718
718 'heads' and 'common' are both lists of revision numbers. If heads is
719 'heads' and 'common' are both lists of revision numbers. If heads is
719 not supplied, uses all of the revlog's heads. If common is not
720 not supplied, uses all of the revlog's heads. If common is not
720 supplied, uses nullid."""
721 supplied, uses nullid."""
721 if common is None:
722 if common is None:
722 common = [nullrev]
723 common = [nullrev]
723 if heads is None:
724 if heads is None:
724 heads = self.headrevs()
725 heads = self.headrevs()
725
726
726 inc = self.incrementalmissingrevs(common=common)
727 inc = self.incrementalmissingrevs(common=common)
727 return inc.missingancestors(heads)
728 return inc.missingancestors(heads)
728
729
729 def findmissing(self, common=None, heads=None):
730 def findmissing(self, common=None, heads=None):
730 """Return the ancestors of heads that are not ancestors of common.
731 """Return the ancestors of heads that are not ancestors of common.
731
732
732 More specifically, return a list of nodes N such that every N
733 More specifically, return a list of nodes N such that every N
733 satisfies the following constraints:
734 satisfies the following constraints:
734
735
735 1. N is an ancestor of some node in 'heads'
736 1. N is an ancestor of some node in 'heads'
736 2. N is not an ancestor of any node in 'common'
737 2. N is not an ancestor of any node in 'common'
737
738
738 The list is sorted by revision number, meaning it is
739 The list is sorted by revision number, meaning it is
739 topologically sorted.
740 topologically sorted.
740
741
741 'heads' and 'common' are both lists of node IDs. If heads is
742 'heads' and 'common' are both lists of node IDs. If heads is
742 not supplied, uses all of the revlog's heads. If common is not
743 not supplied, uses all of the revlog's heads. If common is not
743 supplied, uses nullid."""
744 supplied, uses nullid."""
744 if common is None:
745 if common is None:
745 common = [nullid]
746 common = [nullid]
746 if heads is None:
747 if heads is None:
747 heads = self.heads()
748 heads = self.heads()
748
749
749 common = [self.rev(n) for n in common]
750 common = [self.rev(n) for n in common]
750 heads = [self.rev(n) for n in heads]
751 heads = [self.rev(n) for n in heads]
751
752
752 inc = self.incrementalmissingrevs(common=common)
753 inc = self.incrementalmissingrevs(common=common)
753 return [self.node(r) for r in inc.missingancestors(heads)]
754 return [self.node(r) for r in inc.missingancestors(heads)]
754
755
755 def nodesbetween(self, roots=None, heads=None):
756 def nodesbetween(self, roots=None, heads=None):
756 """Return a topological path from 'roots' to 'heads'.
757 """Return a topological path from 'roots' to 'heads'.
757
758
758 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
759 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
759 topologically sorted list of all nodes N that satisfy both of
760 topologically sorted list of all nodes N that satisfy both of
760 these constraints:
761 these constraints:
761
762
762 1. N is a descendant of some node in 'roots'
763 1. N is a descendant of some node in 'roots'
763 2. N is an ancestor of some node in 'heads'
764 2. N is an ancestor of some node in 'heads'
764
765
765 Every node is considered to be both a descendant and an ancestor
766 Every node is considered to be both a descendant and an ancestor
766 of itself, so every reachable node in 'roots' and 'heads' will be
767 of itself, so every reachable node in 'roots' and 'heads' will be
767 included in 'nodes'.
768 included in 'nodes'.
768
769
769 'outroots' is the list of reachable nodes in 'roots', i.e., the
770 'outroots' is the list of reachable nodes in 'roots', i.e., the
770 subset of 'roots' that is returned in 'nodes'. Likewise,
771 subset of 'roots' that is returned in 'nodes'. Likewise,
771 'outheads' is the subset of 'heads' that is also in 'nodes'.
772 'outheads' is the subset of 'heads' that is also in 'nodes'.
772
773
773 'roots' and 'heads' are both lists of node IDs. If 'roots' is
774 'roots' and 'heads' are both lists of node IDs. If 'roots' is
774 unspecified, uses nullid as the only root. If 'heads' is
775 unspecified, uses nullid as the only root. If 'heads' is
775 unspecified, uses list of all of the revlog's heads."""
776 unspecified, uses list of all of the revlog's heads."""
776 nonodes = ([], [], [])
777 nonodes = ([], [], [])
777 if roots is not None:
778 if roots is not None:
778 roots = list(roots)
779 roots = list(roots)
779 if not roots:
780 if not roots:
780 return nonodes
781 return nonodes
781 lowestrev = min([self.rev(n) for n in roots])
782 lowestrev = min([self.rev(n) for n in roots])
782 else:
783 else:
783 roots = [nullid] # Everybody's a descendant of nullid
784 roots = [nullid] # Everybody's a descendant of nullid
784 lowestrev = nullrev
785 lowestrev = nullrev
785 if (lowestrev == nullrev) and (heads is None):
786 if (lowestrev == nullrev) and (heads is None):
786 # We want _all_ the nodes!
787 # We want _all_ the nodes!
787 return ([self.node(r) for r in self], [nullid], list(self.heads()))
788 return ([self.node(r) for r in self], [nullid], list(self.heads()))
788 if heads is None:
789 if heads is None:
789 # All nodes are ancestors, so the latest ancestor is the last
790 # All nodes are ancestors, so the latest ancestor is the last
790 # node.
791 # node.
791 highestrev = len(self) - 1
792 highestrev = len(self) - 1
792 # Set ancestors to None to signal that every node is an ancestor.
793 # Set ancestors to None to signal that every node is an ancestor.
793 ancestors = None
794 ancestors = None
794 # Set heads to an empty dictionary for later discovery of heads
795 # Set heads to an empty dictionary for later discovery of heads
795 heads = {}
796 heads = {}
796 else:
797 else:
797 heads = list(heads)
798 heads = list(heads)
798 if not heads:
799 if not heads:
799 return nonodes
800 return nonodes
800 ancestors = set()
801 ancestors = set()
801 # Turn heads into a dictionary so we can remove 'fake' heads.
802 # Turn heads into a dictionary so we can remove 'fake' heads.
802 # Also, later we will be using it to filter out the heads we can't
803 # Also, later we will be using it to filter out the heads we can't
803 # find from roots.
804 # find from roots.
804 heads = dict.fromkeys(heads, False)
805 heads = dict.fromkeys(heads, False)
805 # Start at the top and keep marking parents until we're done.
806 # Start at the top and keep marking parents until we're done.
806 nodestotag = set(heads)
807 nodestotag = set(heads)
807 # Remember where the top was so we can use it as a limit later.
808 # Remember where the top was so we can use it as a limit later.
808 highestrev = max([self.rev(n) for n in nodestotag])
809 highestrev = max([self.rev(n) for n in nodestotag])
809 while nodestotag:
810 while nodestotag:
810 # grab a node to tag
811 # grab a node to tag
811 n = nodestotag.pop()
812 n = nodestotag.pop()
812 # Never tag nullid
813 # Never tag nullid
813 if n == nullid:
814 if n == nullid:
814 continue
815 continue
815 # A node's revision number represents its place in a
816 # A node's revision number represents its place in a
816 # topologically sorted list of nodes.
817 # topologically sorted list of nodes.
817 r = self.rev(n)
818 r = self.rev(n)
818 if r >= lowestrev:
819 if r >= lowestrev:
819 if n not in ancestors:
820 if n not in ancestors:
820 # If we are possibly a descendant of one of the roots
821 # If we are possibly a descendant of one of the roots
821 # and we haven't already been marked as an ancestor
822 # and we haven't already been marked as an ancestor
822 ancestors.add(n) # Mark as ancestor
823 ancestors.add(n) # Mark as ancestor
823 # Add non-nullid parents to list of nodes to tag.
824 # Add non-nullid parents to list of nodes to tag.
824 nodestotag.update([p for p in self.parents(n) if
825 nodestotag.update([p for p in self.parents(n) if
825 p != nullid])
826 p != nullid])
826 elif n in heads: # We've seen it before, is it a fake head?
827 elif n in heads: # We've seen it before, is it a fake head?
827 # So it is, real heads should not be the ancestors of
828 # So it is, real heads should not be the ancestors of
828 # any other heads.
829 # any other heads.
829 heads.pop(n)
830 heads.pop(n)
830 if not ancestors:
831 if not ancestors:
831 return nonodes
832 return nonodes
832 # Now that we have our set of ancestors, we want to remove any
833 # Now that we have our set of ancestors, we want to remove any
833 # roots that are not ancestors.
834 # roots that are not ancestors.
834
835
835 # If one of the roots was nullid, everything is included anyway.
836 # If one of the roots was nullid, everything is included anyway.
836 if lowestrev > nullrev:
837 if lowestrev > nullrev:
837 # But, since we weren't, let's recompute the lowest rev to not
838 # But, since we weren't, let's recompute the lowest rev to not
838 # include roots that aren't ancestors.
839 # include roots that aren't ancestors.
839
840
840 # Filter out roots that aren't ancestors of heads
841 # Filter out roots that aren't ancestors of heads
841 roots = [root for root in roots if root in ancestors]
842 roots = [root for root in roots if root in ancestors]
842 # Recompute the lowest revision
843 # Recompute the lowest revision
843 if roots:
844 if roots:
844 lowestrev = min([self.rev(root) for root in roots])
845 lowestrev = min([self.rev(root) for root in roots])
845 else:
846 else:
846 # No more roots? Return empty list
847 # No more roots? Return empty list
847 return nonodes
848 return nonodes
848 else:
849 else:
849 # We are descending from nullid, and don't need to care about
850 # We are descending from nullid, and don't need to care about
850 # any other roots.
851 # any other roots.
851 lowestrev = nullrev
852 lowestrev = nullrev
852 roots = [nullid]
853 roots = [nullid]
853 # Transform our roots list into a set.
854 # Transform our roots list into a set.
854 descendants = set(roots)
855 descendants = set(roots)
855 # Also, keep the original roots so we can filter out roots that aren't
856 # Also, keep the original roots so we can filter out roots that aren't
856 # 'real' roots (i.e. are descended from other roots).
857 # 'real' roots (i.e. are descended from other roots).
857 roots = descendants.copy()
858 roots = descendants.copy()
858 # Our topologically sorted list of output nodes.
859 # Our topologically sorted list of output nodes.
859 orderedout = []
860 orderedout = []
860 # Don't start at nullid since we don't want nullid in our output list,
861 # Don't start at nullid since we don't want nullid in our output list,
861 # and if nullid shows up in descendants, empty parents will look like
862 # and if nullid shows up in descendants, empty parents will look like
862 # they're descendants.
863 # they're descendants.
863 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
864 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
864 n = self.node(r)
865 n = self.node(r)
865 isdescendant = False
866 isdescendant = False
866 if lowestrev == nullrev: # Everybody is a descendant of nullid
867 if lowestrev == nullrev: # Everybody is a descendant of nullid
867 isdescendant = True
868 isdescendant = True
868 elif n in descendants:
869 elif n in descendants:
869 # n is already a descendant
870 # n is already a descendant
870 isdescendant = True
871 isdescendant = True
871 # This check only needs to be done here because all the roots
872 # This check only needs to be done here because all the roots
872 # will start being marked is descendants before the loop.
873 # will start being marked is descendants before the loop.
873 if n in roots:
874 if n in roots:
874 # If n was a root, check if it's a 'real' root.
875 # If n was a root, check if it's a 'real' root.
875 p = tuple(self.parents(n))
876 p = tuple(self.parents(n))
876 # If any of its parents are descendants, it's not a root.
877 # If any of its parents are descendants, it's not a root.
877 if (p[0] in descendants) or (p[1] in descendants):
878 if (p[0] in descendants) or (p[1] in descendants):
878 roots.remove(n)
879 roots.remove(n)
879 else:
880 else:
880 p = tuple(self.parents(n))
881 p = tuple(self.parents(n))
881 # A node is a descendant if either of its parents are
882 # A node is a descendant if either of its parents are
882 # descendants. (We seeded the dependents list with the roots
883 # descendants. (We seeded the dependents list with the roots
883 # up there, remember?)
884 # up there, remember?)
884 if (p[0] in descendants) or (p[1] in descendants):
885 if (p[0] in descendants) or (p[1] in descendants):
885 descendants.add(n)
886 descendants.add(n)
886 isdescendant = True
887 isdescendant = True
887 if isdescendant and ((ancestors is None) or (n in ancestors)):
888 if isdescendant and ((ancestors is None) or (n in ancestors)):
888 # Only include nodes that are both descendants and ancestors.
889 # Only include nodes that are both descendants and ancestors.
889 orderedout.append(n)
890 orderedout.append(n)
890 if (ancestors is not None) and (n in heads):
891 if (ancestors is not None) and (n in heads):
891 # We're trying to figure out which heads are reachable
892 # We're trying to figure out which heads are reachable
892 # from roots.
893 # from roots.
893 # Mark this head as having been reached
894 # Mark this head as having been reached
894 heads[n] = True
895 heads[n] = True
895 elif ancestors is None:
896 elif ancestors is None:
896 # Otherwise, we're trying to discover the heads.
897 # Otherwise, we're trying to discover the heads.
897 # Assume this is a head because if it isn't, the next step
898 # Assume this is a head because if it isn't, the next step
898 # will eventually remove it.
899 # will eventually remove it.
899 heads[n] = True
900 heads[n] = True
900 # But, obviously its parents aren't.
901 # But, obviously its parents aren't.
901 for p in self.parents(n):
902 for p in self.parents(n):
902 heads.pop(p, None)
903 heads.pop(p, None)
903 heads = [head for head, flag in heads.iteritems() if flag]
904 heads = [head for head, flag in heads.iteritems() if flag]
904 roots = list(roots)
905 roots = list(roots)
905 assert orderedout
906 assert orderedout
906 assert roots
907 assert roots
907 assert heads
908 assert heads
908 return (orderedout, roots, heads)
909 return (orderedout, roots, heads)
909
910
910 def headrevs(self):
911 def headrevs(self):
911 try:
912 try:
912 return self.index.headrevs()
913 return self.index.headrevs()
913 except AttributeError:
914 except AttributeError:
914 return self._headrevs()
915 return self._headrevs()
915
916
916 def computephases(self, roots):
917 def computephases(self, roots):
917 return self.index.computephasesmapsets(roots)
918 return self.index.computephasesmapsets(roots)
918
919
919 def _headrevs(self):
920 def _headrevs(self):
920 count = len(self)
921 count = len(self)
921 if not count:
922 if not count:
922 return [nullrev]
923 return [nullrev]
923 # we won't iter over filtered rev so nobody is a head at start
924 # we won't iter over filtered rev so nobody is a head at start
924 ishead = [0] * (count + 1)
925 ishead = [0] * (count + 1)
925 index = self.index
926 index = self.index
926 for r in self:
927 for r in self:
927 ishead[r] = 1 # I may be an head
928 ishead[r] = 1 # I may be an head
928 e = index[r]
929 e = index[r]
929 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
930 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
930 return [r for r, val in enumerate(ishead) if val]
931 return [r for r, val in enumerate(ishead) if val]
931
932
932 def heads(self, start=None, stop=None):
933 def heads(self, start=None, stop=None):
933 """return the list of all nodes that have no children
934 """return the list of all nodes that have no children
934
935
935 if start is specified, only heads that are descendants of
936 if start is specified, only heads that are descendants of
936 start will be returned
937 start will be returned
937 if stop is specified, it will consider all the revs from stop
938 if stop is specified, it will consider all the revs from stop
938 as if they had no children
939 as if they had no children
939 """
940 """
940 if start is None and stop is None:
941 if start is None and stop is None:
941 if not len(self):
942 if not len(self):
942 return [nullid]
943 return [nullid]
943 return [self.node(r) for r in self.headrevs()]
944 return [self.node(r) for r in self.headrevs()]
944
945
945 if start is None:
946 if start is None:
946 start = nullid
947 start = nullid
947 if stop is None:
948 if stop is None:
948 stop = []
949 stop = []
949 stoprevs = set([self.rev(n) for n in stop])
950 stoprevs = set([self.rev(n) for n in stop])
950 startrev = self.rev(start)
951 startrev = self.rev(start)
951 reachable = {startrev}
952 reachable = {startrev}
952 heads = {startrev}
953 heads = {startrev}
953
954
954 parentrevs = self.parentrevs
955 parentrevs = self.parentrevs
955 for r in self.revs(start=startrev + 1):
956 for r in self.revs(start=startrev + 1):
956 for p in parentrevs(r):
957 for p in parentrevs(r):
957 if p in reachable:
958 if p in reachable:
958 if r not in stoprevs:
959 if r not in stoprevs:
959 reachable.add(r)
960 reachable.add(r)
960 heads.add(r)
961 heads.add(r)
961 if p in heads and p not in stoprevs:
962 if p in heads and p not in stoprevs:
962 heads.remove(p)
963 heads.remove(p)
963
964
964 return [self.node(r) for r in heads]
965 return [self.node(r) for r in heads]
965
966
966 def children(self, node):
967 def children(self, node):
967 """find the children of a given node"""
968 """find the children of a given node"""
968 c = []
969 c = []
969 p = self.rev(node)
970 p = self.rev(node)
970 for r in self.revs(start=p + 1):
971 for r in self.revs(start=p + 1):
971 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
972 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
972 if prevs:
973 if prevs:
973 for pr in prevs:
974 for pr in prevs:
974 if pr == p:
975 if pr == p:
975 c.append(self.node(r))
976 c.append(self.node(r))
976 elif p == nullrev:
977 elif p == nullrev:
977 c.append(self.node(r))
978 c.append(self.node(r))
978 return c
979 return c
979
980
980 def descendant(self, start, end):
981 def descendant(self, start, end):
981 if start == nullrev:
982 if start == nullrev:
982 return True
983 return True
983 for i in self.descendants([start]):
984 for i in self.descendants([start]):
984 if i == end:
985 if i == end:
985 return True
986 return True
986 elif i > end:
987 elif i > end:
987 break
988 break
988 return False
989 return False
989
990
990 def commonancestorsheads(self, a, b):
991 def commonancestorsheads(self, a, b):
991 """calculate all the heads of the common ancestors of nodes a and b"""
992 """calculate all the heads of the common ancestors of nodes a and b"""
992 a, b = self.rev(a), self.rev(b)
993 a, b = self.rev(a), self.rev(b)
993 try:
994 try:
994 ancs = self.index.commonancestorsheads(a, b)
995 ancs = self.index.commonancestorsheads(a, b)
995 except (AttributeError, OverflowError): # C implementation failed
996 except (AttributeError, OverflowError): # C implementation failed
996 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
997 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
997 return pycompat.maplist(self.node, ancs)
998 return pycompat.maplist(self.node, ancs)
998
999
999 def isancestor(self, a, b):
1000 def isancestor(self, a, b):
1000 """return True if node a is an ancestor of node b
1001 """return True if node a is an ancestor of node b
1001
1002
1002 The implementation of this is trivial but the use of
1003 The implementation of this is trivial but the use of
1003 commonancestorsheads is not."""
1004 commonancestorsheads is not."""
1004 return a in self.commonancestorsheads(a, b)
1005 return a in self.commonancestorsheads(a, b)
1005
1006
1006 def ancestor(self, a, b):
1007 def ancestor(self, a, b):
1007 """calculate the "best" common ancestor of nodes a and b"""
1008 """calculate the "best" common ancestor of nodes a and b"""
1008
1009
1009 a, b = self.rev(a), self.rev(b)
1010 a, b = self.rev(a), self.rev(b)
1010 try:
1011 try:
1011 ancs = self.index.ancestors(a, b)
1012 ancs = self.index.ancestors(a, b)
1012 except (AttributeError, OverflowError):
1013 except (AttributeError, OverflowError):
1013 ancs = ancestor.ancestors(self.parentrevs, a, b)
1014 ancs = ancestor.ancestors(self.parentrevs, a, b)
1014 if ancs:
1015 if ancs:
1015 # choose a consistent winner when there's a tie
1016 # choose a consistent winner when there's a tie
1016 return min(map(self.node, ancs))
1017 return min(map(self.node, ancs))
1017 return nullid
1018 return nullid
1018
1019
1019 def _match(self, id):
1020 def _match(self, id):
1020 if isinstance(id, int):
1021 if isinstance(id, int):
1021 # rev
1022 # rev
1022 return self.node(id)
1023 return self.node(id)
1023 if len(id) == 20:
1024 if len(id) == 20:
1024 # possibly a binary node
1025 # possibly a binary node
1025 # odds of a binary node being all hex in ASCII are 1 in 10**25
1026 # odds of a binary node being all hex in ASCII are 1 in 10**25
1026 try:
1027 try:
1027 node = id
1028 node = id
1028 self.rev(node) # quick search the index
1029 self.rev(node) # quick search the index
1029 return node
1030 return node
1030 except LookupError:
1031 except LookupError:
1031 pass # may be partial hex id
1032 pass # may be partial hex id
1032 try:
1033 try:
1033 # str(rev)
1034 # str(rev)
1034 rev = int(id)
1035 rev = int(id)
1035 if str(rev) != id:
1036 if str(rev) != id:
1036 raise ValueError
1037 raise ValueError
1037 if rev < 0:
1038 if rev < 0:
1038 rev = len(self) + rev
1039 rev = len(self) + rev
1039 if rev < 0 or rev >= len(self):
1040 if rev < 0 or rev >= len(self):
1040 raise ValueError
1041 raise ValueError
1041 return self.node(rev)
1042 return self.node(rev)
1042 except (ValueError, OverflowError):
1043 except (ValueError, OverflowError):
1043 pass
1044 pass
1044 if len(id) == 40:
1045 if len(id) == 40:
1045 try:
1046 try:
1046 # a full hex nodeid?
1047 # a full hex nodeid?
1047 node = bin(id)
1048 node = bin(id)
1048 self.rev(node)
1049 self.rev(node)
1049 return node
1050 return node
1050 except (TypeError, LookupError):
1051 except (TypeError, LookupError):
1051 pass
1052 pass
1052
1053
1053 def _partialmatch(self, id):
1054 def _partialmatch(self, id):
1054 maybewdir = wdirhex.startswith(id)
1055 maybewdir = wdirhex.startswith(id)
1055 try:
1056 try:
1056 partial = self.index.partialmatch(id)
1057 partial = self.index.partialmatch(id)
1057 if partial and self.hasnode(partial):
1058 if partial and self.hasnode(partial):
1058 if maybewdir:
1059 if maybewdir:
1059 # single 'ff...' match in radix tree, ambiguous with wdir
1060 # single 'ff...' match in radix tree, ambiguous with wdir
1060 raise RevlogError
1061 raise RevlogError
1061 return partial
1062 return partial
1062 if maybewdir:
1063 if maybewdir:
1063 # no 'ff...' match in radix tree, wdir identified
1064 # no 'ff...' match in radix tree, wdir identified
1064 raise error.WdirUnsupported
1065 raise error.WdirUnsupported
1065 return None
1066 return None
1066 except RevlogError:
1067 except RevlogError:
1067 # parsers.c radix tree lookup gave multiple matches
1068 # parsers.c radix tree lookup gave multiple matches
1068 # fast path: for unfiltered changelog, radix tree is accurate
1069 # fast path: for unfiltered changelog, radix tree is accurate
1069 if not getattr(self, 'filteredrevs', None):
1070 if not getattr(self, 'filteredrevs', None):
1070 raise LookupError(id, self.indexfile,
1071 raise LookupError(id, self.indexfile,
1071 _('ambiguous identifier'))
1072 _('ambiguous identifier'))
1072 # fall through to slow path that filters hidden revisions
1073 # fall through to slow path that filters hidden revisions
1073 except (AttributeError, ValueError):
1074 except (AttributeError, ValueError):
1074 # we are pure python, or key was too short to search radix tree
1075 # we are pure python, or key was too short to search radix tree
1075 pass
1076 pass
1076
1077
1077 if id in self._pcache:
1078 if id in self._pcache:
1078 return self._pcache[id]
1079 return self._pcache[id]
1079
1080
1080 if len(id) < 40:
1081 if len(id) < 40:
1081 try:
1082 try:
1082 # hex(node)[:...]
1083 # hex(node)[:...]
1083 l = len(id) // 2 # grab an even number of digits
1084 l = len(id) // 2 # grab an even number of digits
1084 prefix = bin(id[:l * 2])
1085 prefix = bin(id[:l * 2])
1085 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1086 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1086 nl = [n for n in nl if hex(n).startswith(id) and
1087 nl = [n for n in nl if hex(n).startswith(id) and
1087 self.hasnode(n)]
1088 self.hasnode(n)]
1088 if len(nl) > 0:
1089 if len(nl) > 0:
1089 if len(nl) == 1 and not maybewdir:
1090 if len(nl) == 1 and not maybewdir:
1090 self._pcache[id] = nl[0]
1091 self._pcache[id] = nl[0]
1091 return nl[0]
1092 return nl[0]
1092 raise LookupError(id, self.indexfile,
1093 raise LookupError(id, self.indexfile,
1093 _('ambiguous identifier'))
1094 _('ambiguous identifier'))
1094 if maybewdir:
1095 if maybewdir:
1095 raise error.WdirUnsupported
1096 raise error.WdirUnsupported
1096 return None
1097 return None
1097 except TypeError:
1098 except (TypeError, binascii.Error):
1098 pass
1099 pass
1099
1100
1100 def lookup(self, id):
1101 def lookup(self, id):
1101 """locate a node based on:
1102 """locate a node based on:
1102 - revision number or str(revision number)
1103 - revision number or str(revision number)
1103 - nodeid or subset of hex nodeid
1104 - nodeid or subset of hex nodeid
1104 """
1105 """
1105 n = self._match(id)
1106 n = self._match(id)
1106 if n is not None:
1107 if n is not None:
1107 return n
1108 return n
1108 n = self._partialmatch(id)
1109 n = self._partialmatch(id)
1109 if n:
1110 if n:
1110 return n
1111 return n
1111
1112
1112 raise LookupError(id, self.indexfile, _('no match found'))
1113 raise LookupError(id, self.indexfile, _('no match found'))
1113
1114
1114 def cmp(self, node, text):
1115 def cmp(self, node, text):
1115 """compare text with a given file revision
1116 """compare text with a given file revision
1116
1117
1117 returns True if text is different than what is stored.
1118 returns True if text is different than what is stored.
1118 """
1119 """
1119 p1, p2 = self.parents(node)
1120 p1, p2 = self.parents(node)
1120 return hash(text, p1, p2) != node
1121 return hash(text, p1, p2) != node
1121
1122
1122 def _cachesegment(self, offset, data):
1123 def _cachesegment(self, offset, data):
1123 """Add a segment to the revlog cache.
1124 """Add a segment to the revlog cache.
1124
1125
1125 Accepts an absolute offset and the data that is at that location.
1126 Accepts an absolute offset and the data that is at that location.
1126 """
1127 """
1127 o, d = self._chunkcache
1128 o, d = self._chunkcache
1128 # try to add to existing cache
1129 # try to add to existing cache
1129 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1130 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1130 self._chunkcache = o, d + data
1131 self._chunkcache = o, d + data
1131 else:
1132 else:
1132 self._chunkcache = offset, data
1133 self._chunkcache = offset, data
1133
1134
1134 def _readsegment(self, offset, length, df=None):
1135 def _readsegment(self, offset, length, df=None):
1135 """Load a segment of raw data from the revlog.
1136 """Load a segment of raw data from the revlog.
1136
1137
1137 Accepts an absolute offset, length to read, and an optional existing
1138 Accepts an absolute offset, length to read, and an optional existing
1138 file handle to read from.
1139 file handle to read from.
1139
1140
1140 If an existing file handle is passed, it will be seeked and the
1141 If an existing file handle is passed, it will be seeked and the
1141 original seek position will NOT be restored.
1142 original seek position will NOT be restored.
1142
1143
1143 Returns a str or buffer of raw byte data.
1144 Returns a str or buffer of raw byte data.
1144 """
1145 """
1145 if df is not None:
1146 if df is not None:
1146 closehandle = False
1147 closehandle = False
1147 else:
1148 else:
1148 if self._inline:
1149 if self._inline:
1149 df = self.opener(self.indexfile)
1150 df = self.opener(self.indexfile)
1150 else:
1151 else:
1151 df = self.opener(self.datafile)
1152 df = self.opener(self.datafile)
1152 closehandle = True
1153 closehandle = True
1153
1154
1154 # Cache data both forward and backward around the requested
1155 # Cache data both forward and backward around the requested
1155 # data, in a fixed size window. This helps speed up operations
1156 # data, in a fixed size window. This helps speed up operations
1156 # involving reading the revlog backwards.
1157 # involving reading the revlog backwards.
1157 cachesize = self._chunkcachesize
1158 cachesize = self._chunkcachesize
1158 realoffset = offset & ~(cachesize - 1)
1159 realoffset = offset & ~(cachesize - 1)
1159 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1160 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1160 - realoffset)
1161 - realoffset)
1161 df.seek(realoffset)
1162 df.seek(realoffset)
1162 d = df.read(reallength)
1163 d = df.read(reallength)
1163 if closehandle:
1164 if closehandle:
1164 df.close()
1165 df.close()
1165 self._cachesegment(realoffset, d)
1166 self._cachesegment(realoffset, d)
1166 if offset != realoffset or reallength != length:
1167 if offset != realoffset or reallength != length:
1167 return util.buffer(d, offset - realoffset, length)
1168 return util.buffer(d, offset - realoffset, length)
1168 return d
1169 return d
1169
1170
1170 def _getsegment(self, offset, length, df=None):
1171 def _getsegment(self, offset, length, df=None):
1171 """Obtain a segment of raw data from the revlog.
1172 """Obtain a segment of raw data from the revlog.
1172
1173
1173 Accepts an absolute offset, length of bytes to obtain, and an
1174 Accepts an absolute offset, length of bytes to obtain, and an
1174 optional file handle to the already-opened revlog. If the file
1175 optional file handle to the already-opened revlog. If the file
1175 handle is used, it's original seek position will not be preserved.
1176 handle is used, it's original seek position will not be preserved.
1176
1177
1177 Requests for data may be returned from a cache.
1178 Requests for data may be returned from a cache.
1178
1179
1179 Returns a str or a buffer instance of raw byte data.
1180 Returns a str or a buffer instance of raw byte data.
1180 """
1181 """
1181 o, d = self._chunkcache
1182 o, d = self._chunkcache
1182 l = len(d)
1183 l = len(d)
1183
1184
1184 # is it in the cache?
1185 # is it in the cache?
1185 cachestart = offset - o
1186 cachestart = offset - o
1186 cacheend = cachestart + length
1187 cacheend = cachestart + length
1187 if cachestart >= 0 and cacheend <= l:
1188 if cachestart >= 0 and cacheend <= l:
1188 if cachestart == 0 and cacheend == l:
1189 if cachestart == 0 and cacheend == l:
1189 return d # avoid a copy
1190 return d # avoid a copy
1190 return util.buffer(d, cachestart, cacheend - cachestart)
1191 return util.buffer(d, cachestart, cacheend - cachestart)
1191
1192
1192 return self._readsegment(offset, length, df=df)
1193 return self._readsegment(offset, length, df=df)
1193
1194
1194 def _getsegmentforrevs(self, startrev, endrev, df=None):
1195 def _getsegmentforrevs(self, startrev, endrev, df=None):
1195 """Obtain a segment of raw data corresponding to a range of revisions.
1196 """Obtain a segment of raw data corresponding to a range of revisions.
1196
1197
1197 Accepts the start and end revisions and an optional already-open
1198 Accepts the start and end revisions and an optional already-open
1198 file handle to be used for reading. If the file handle is read, its
1199 file handle to be used for reading. If the file handle is read, its
1199 seek position will not be preserved.
1200 seek position will not be preserved.
1200
1201
1201 Requests for data may be satisfied by a cache.
1202 Requests for data may be satisfied by a cache.
1202
1203
1203 Returns a 2-tuple of (offset, data) for the requested range of
1204 Returns a 2-tuple of (offset, data) for the requested range of
1204 revisions. Offset is the integer offset from the beginning of the
1205 revisions. Offset is the integer offset from the beginning of the
1205 revlog and data is a str or buffer of the raw byte data.
1206 revlog and data is a str or buffer of the raw byte data.
1206
1207
1207 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1208 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1208 to determine where each revision's data begins and ends.
1209 to determine where each revision's data begins and ends.
1209 """
1210 """
1210 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1211 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1211 # (functions are expensive).
1212 # (functions are expensive).
1212 index = self.index
1213 index = self.index
1213 istart = index[startrev]
1214 istart = index[startrev]
1214 start = int(istart[0] >> 16)
1215 start = int(istart[0] >> 16)
1215 if startrev == endrev:
1216 if startrev == endrev:
1216 end = start + istart[1]
1217 end = start + istart[1]
1217 else:
1218 else:
1218 iend = index[endrev]
1219 iend = index[endrev]
1219 end = int(iend[0] >> 16) + iend[1]
1220 end = int(iend[0] >> 16) + iend[1]
1220
1221
1221 if self._inline:
1222 if self._inline:
1222 start += (startrev + 1) * self._io.size
1223 start += (startrev + 1) * self._io.size
1223 end += (endrev + 1) * self._io.size
1224 end += (endrev + 1) * self._io.size
1224 length = end - start
1225 length = end - start
1225
1226
1226 return start, self._getsegment(start, length, df=df)
1227 return start, self._getsegment(start, length, df=df)
1227
1228
1228 def _chunk(self, rev, df=None):
1229 def _chunk(self, rev, df=None):
1229 """Obtain a single decompressed chunk for a revision.
1230 """Obtain a single decompressed chunk for a revision.
1230
1231
1231 Accepts an integer revision and an optional already-open file handle
1232 Accepts an integer revision and an optional already-open file handle
1232 to be used for reading. If used, the seek position of the file will not
1233 to be used for reading. If used, the seek position of the file will not
1233 be preserved.
1234 be preserved.
1234
1235
1235 Returns a str holding uncompressed data for the requested revision.
1236 Returns a str holding uncompressed data for the requested revision.
1236 """
1237 """
1237 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1238 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1238
1239
1239 def _chunks(self, revs, df=None):
1240 def _chunks(self, revs, df=None):
1240 """Obtain decompressed chunks for the specified revisions.
1241 """Obtain decompressed chunks for the specified revisions.
1241
1242
1242 Accepts an iterable of numeric revisions that are assumed to be in
1243 Accepts an iterable of numeric revisions that are assumed to be in
1243 ascending order. Also accepts an optional already-open file handle
1244 ascending order. Also accepts an optional already-open file handle
1244 to be used for reading. If used, the seek position of the file will
1245 to be used for reading. If used, the seek position of the file will
1245 not be preserved.
1246 not be preserved.
1246
1247
1247 This function is similar to calling ``self._chunk()`` multiple times,
1248 This function is similar to calling ``self._chunk()`` multiple times,
1248 but is faster.
1249 but is faster.
1249
1250
1250 Returns a list with decompressed data for each requested revision.
1251 Returns a list with decompressed data for each requested revision.
1251 """
1252 """
1252 if not revs:
1253 if not revs:
1253 return []
1254 return []
1254 start = self.start
1255 start = self.start
1255 length = self.length
1256 length = self.length
1256 inline = self._inline
1257 inline = self._inline
1257 iosize = self._io.size
1258 iosize = self._io.size
1258 buffer = util.buffer
1259 buffer = util.buffer
1259
1260
1260 l = []
1261 l = []
1261 ladd = l.append
1262 ladd = l.append
1262
1263
1263 try:
1264 try:
1264 offset, data = self._getsegmentforrevs(revs[0], revs[-1], df=df)
1265 offset, data = self._getsegmentforrevs(revs[0], revs[-1], df=df)
1265 except OverflowError:
1266 except OverflowError:
1266 # issue4215 - we can't cache a run of chunks greater than
1267 # issue4215 - we can't cache a run of chunks greater than
1267 # 2G on Windows
1268 # 2G on Windows
1268 return [self._chunk(rev, df=df) for rev in revs]
1269 return [self._chunk(rev, df=df) for rev in revs]
1269
1270
1270 decomp = self.decompress
1271 decomp = self.decompress
1271 for rev in revs:
1272 for rev in revs:
1272 chunkstart = start(rev)
1273 chunkstart = start(rev)
1273 if inline:
1274 if inline:
1274 chunkstart += (rev + 1) * iosize
1275 chunkstart += (rev + 1) * iosize
1275 chunklength = length(rev)
1276 chunklength = length(rev)
1276 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1277 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1277
1278
1278 return l
1279 return l
1279
1280
1280 def _chunkclear(self):
1281 def _chunkclear(self):
1281 """Clear the raw chunk cache."""
1282 """Clear the raw chunk cache."""
1282 self._chunkcache = (0, '')
1283 self._chunkcache = (0, '')
1283
1284
1284 def deltaparent(self, rev):
1285 def deltaparent(self, rev):
1285 """return deltaparent of the given revision"""
1286 """return deltaparent of the given revision"""
1286 base = self.index[rev][3]
1287 base = self.index[rev][3]
1287 if base == rev:
1288 if base == rev:
1288 return nullrev
1289 return nullrev
1289 elif self._generaldelta:
1290 elif self._generaldelta:
1290 return base
1291 return base
1291 else:
1292 else:
1292 return rev - 1
1293 return rev - 1
1293
1294
1294 def revdiff(self, rev1, rev2):
1295 def revdiff(self, rev1, rev2):
1295 """return or calculate a delta between two revisions
1296 """return or calculate a delta between two revisions
1296
1297
1297 The delta calculated is in binary form and is intended to be written to
1298 The delta calculated is in binary form and is intended to be written to
1298 revlog data directly. So this function needs raw revision data.
1299 revlog data directly. So this function needs raw revision data.
1299 """
1300 """
1300 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1301 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1301 return bytes(self._chunk(rev2))
1302 return bytes(self._chunk(rev2))
1302
1303
1303 return mdiff.textdiff(self.revision(rev1, raw=True),
1304 return mdiff.textdiff(self.revision(rev1, raw=True),
1304 self.revision(rev2, raw=True))
1305 self.revision(rev2, raw=True))
1305
1306
1306 def revision(self, nodeorrev, _df=None, raw=False):
1307 def revision(self, nodeorrev, _df=None, raw=False):
1307 """return an uncompressed revision of a given node or revision
1308 """return an uncompressed revision of a given node or revision
1308 number.
1309 number.
1309
1310
1310 _df - an existing file handle to read from. (internal-only)
1311 _df - an existing file handle to read from. (internal-only)
1311 raw - an optional argument specifying if the revision data is to be
1312 raw - an optional argument specifying if the revision data is to be
1312 treated as raw data when applying flag transforms. 'raw' should be set
1313 treated as raw data when applying flag transforms. 'raw' should be set
1313 to True when generating changegroups or in debug commands.
1314 to True when generating changegroups or in debug commands.
1314 """
1315 """
1315 if isinstance(nodeorrev, int):
1316 if isinstance(nodeorrev, int):
1316 rev = nodeorrev
1317 rev = nodeorrev
1317 node = self.node(rev)
1318 node = self.node(rev)
1318 else:
1319 else:
1319 node = nodeorrev
1320 node = nodeorrev
1320 rev = None
1321 rev = None
1321
1322
1322 cachedrev = None
1323 cachedrev = None
1323 flags = None
1324 flags = None
1324 rawtext = None
1325 rawtext = None
1325 if node == nullid:
1326 if node == nullid:
1326 return ""
1327 return ""
1327 if self._cache:
1328 if self._cache:
1328 if self._cache[0] == node:
1329 if self._cache[0] == node:
1329 # _cache only stores rawtext
1330 # _cache only stores rawtext
1330 if raw:
1331 if raw:
1331 return self._cache[2]
1332 return self._cache[2]
1332 # duplicated, but good for perf
1333 # duplicated, but good for perf
1333 if rev is None:
1334 if rev is None:
1334 rev = self.rev(node)
1335 rev = self.rev(node)
1335 if flags is None:
1336 if flags is None:
1336 flags = self.flags(rev)
1337 flags = self.flags(rev)
1337 # no extra flags set, no flag processor runs, text = rawtext
1338 # no extra flags set, no flag processor runs, text = rawtext
1338 if flags == REVIDX_DEFAULT_FLAGS:
1339 if flags == REVIDX_DEFAULT_FLAGS:
1339 return self._cache[2]
1340 return self._cache[2]
1340 # rawtext is reusable. need to run flag processor
1341 # rawtext is reusable. need to run flag processor
1341 rawtext = self._cache[2]
1342 rawtext = self._cache[2]
1342
1343
1343 cachedrev = self._cache[1]
1344 cachedrev = self._cache[1]
1344
1345
1345 # look up what we need to read
1346 # look up what we need to read
1346 if rawtext is None:
1347 if rawtext is None:
1347 if rev is None:
1348 if rev is None:
1348 rev = self.rev(node)
1349 rev = self.rev(node)
1349
1350
1350 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1351 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1351 if stopped:
1352 if stopped:
1352 rawtext = self._cache[2]
1353 rawtext = self._cache[2]
1353
1354
1354 # drop cache to save memory
1355 # drop cache to save memory
1355 self._cache = None
1356 self._cache = None
1356
1357
1357 bins = self._chunks(chain, df=_df)
1358 bins = self._chunks(chain, df=_df)
1358 if rawtext is None:
1359 if rawtext is None:
1359 rawtext = bytes(bins[0])
1360 rawtext = bytes(bins[0])
1360 bins = bins[1:]
1361 bins = bins[1:]
1361
1362
1362 rawtext = mdiff.patches(rawtext, bins)
1363 rawtext = mdiff.patches(rawtext, bins)
1363 self._cache = (node, rev, rawtext)
1364 self._cache = (node, rev, rawtext)
1364
1365
1365 if flags is None:
1366 if flags is None:
1366 if rev is None:
1367 if rev is None:
1367 rev = self.rev(node)
1368 rev = self.rev(node)
1368 flags = self.flags(rev)
1369 flags = self.flags(rev)
1369
1370
1370 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1371 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1371 if validatehash:
1372 if validatehash:
1372 self.checkhash(text, node, rev=rev)
1373 self.checkhash(text, node, rev=rev)
1373
1374
1374 return text
1375 return text
1375
1376
1376 def hash(self, text, p1, p2):
1377 def hash(self, text, p1, p2):
1377 """Compute a node hash.
1378 """Compute a node hash.
1378
1379
1379 Available as a function so that subclasses can replace the hash
1380 Available as a function so that subclasses can replace the hash
1380 as needed.
1381 as needed.
1381 """
1382 """
1382 return hash(text, p1, p2)
1383 return hash(text, p1, p2)
1383
1384
1384 def _processflags(self, text, flags, operation, raw=False):
1385 def _processflags(self, text, flags, operation, raw=False):
1385 """Inspect revision data flags and applies transforms defined by
1386 """Inspect revision data flags and applies transforms defined by
1386 registered flag processors.
1387 registered flag processors.
1387
1388
1388 ``text`` - the revision data to process
1389 ``text`` - the revision data to process
1389 ``flags`` - the revision flags
1390 ``flags`` - the revision flags
1390 ``operation`` - the operation being performed (read or write)
1391 ``operation`` - the operation being performed (read or write)
1391 ``raw`` - an optional argument describing if the raw transform should be
1392 ``raw`` - an optional argument describing if the raw transform should be
1392 applied.
1393 applied.
1393
1394
1394 This method processes the flags in the order (or reverse order if
1395 This method processes the flags in the order (or reverse order if
1395 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1396 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1396 flag processors registered for present flags. The order of flags defined
1397 flag processors registered for present flags. The order of flags defined
1397 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1398 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1398
1399
1399 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1400 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1400 processed text and ``validatehash`` is a bool indicating whether the
1401 processed text and ``validatehash`` is a bool indicating whether the
1401 returned text should be checked for hash integrity.
1402 returned text should be checked for hash integrity.
1402
1403
1403 Note: If the ``raw`` argument is set, it has precedence over the
1404 Note: If the ``raw`` argument is set, it has precedence over the
1404 operation and will only update the value of ``validatehash``.
1405 operation and will only update the value of ``validatehash``.
1405 """
1406 """
1406 # fast path: no flag processors will run
1407 # fast path: no flag processors will run
1407 if flags == 0:
1408 if flags == 0:
1408 return text, True
1409 return text, True
1409 if not operation in ('read', 'write'):
1410 if not operation in ('read', 'write'):
1410 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
1411 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
1411 # Check all flags are known.
1412 # Check all flags are known.
1412 if flags & ~REVIDX_KNOWN_FLAGS:
1413 if flags & ~REVIDX_KNOWN_FLAGS:
1413 raise RevlogError(_("incompatible revision flag '%#x'") %
1414 raise RevlogError(_("incompatible revision flag '%#x'") %
1414 (flags & ~REVIDX_KNOWN_FLAGS))
1415 (flags & ~REVIDX_KNOWN_FLAGS))
1415 validatehash = True
1416 validatehash = True
1416 # Depending on the operation (read or write), the order might be
1417 # Depending on the operation (read or write), the order might be
1417 # reversed due to non-commutative transforms.
1418 # reversed due to non-commutative transforms.
1418 orderedflags = REVIDX_FLAGS_ORDER
1419 orderedflags = REVIDX_FLAGS_ORDER
1419 if operation == 'write':
1420 if operation == 'write':
1420 orderedflags = reversed(orderedflags)
1421 orderedflags = reversed(orderedflags)
1421
1422
1422 for flag in orderedflags:
1423 for flag in orderedflags:
1423 # If a flagprocessor has been registered for a known flag, apply the
1424 # If a flagprocessor has been registered for a known flag, apply the
1424 # related operation transform and update result tuple.
1425 # related operation transform and update result tuple.
1425 if flag & flags:
1426 if flag & flags:
1426 vhash = True
1427 vhash = True
1427
1428
1428 if flag not in _flagprocessors:
1429 if flag not in _flagprocessors:
1429 message = _("missing processor for flag '%#x'") % (flag)
1430 message = _("missing processor for flag '%#x'") % (flag)
1430 raise RevlogError(message)
1431 raise RevlogError(message)
1431
1432
1432 processor = _flagprocessors[flag]
1433 processor = _flagprocessors[flag]
1433 if processor is not None:
1434 if processor is not None:
1434 readtransform, writetransform, rawtransform = processor
1435 readtransform, writetransform, rawtransform = processor
1435
1436
1436 if raw:
1437 if raw:
1437 vhash = rawtransform(self, text)
1438 vhash = rawtransform(self, text)
1438 elif operation == 'read':
1439 elif operation == 'read':
1439 text, vhash = readtransform(self, text)
1440 text, vhash = readtransform(self, text)
1440 else: # write operation
1441 else: # write operation
1441 text, vhash = writetransform(self, text)
1442 text, vhash = writetransform(self, text)
1442 validatehash = validatehash and vhash
1443 validatehash = validatehash and vhash
1443
1444
1444 return text, validatehash
1445 return text, validatehash
1445
1446
1446 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1447 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1447 """Check node hash integrity.
1448 """Check node hash integrity.
1448
1449
1449 Available as a function so that subclasses can extend hash mismatch
1450 Available as a function so that subclasses can extend hash mismatch
1450 behaviors as needed.
1451 behaviors as needed.
1451 """
1452 """
1452 if p1 is None and p2 is None:
1453 if p1 is None and p2 is None:
1453 p1, p2 = self.parents(node)
1454 p1, p2 = self.parents(node)
1454 if node != self.hash(text, p1, p2):
1455 if node != self.hash(text, p1, p2):
1455 revornode = rev
1456 revornode = rev
1456 if revornode is None:
1457 if revornode is None:
1457 revornode = templatefilters.short(hex(node))
1458 revornode = templatefilters.short(hex(node))
1458 raise RevlogError(_("integrity check failed on %s:%s")
1459 raise RevlogError(_("integrity check failed on %s:%s")
1459 % (self.indexfile, revornode))
1460 % (self.indexfile, revornode))
1460
1461
1461 def checkinlinesize(self, tr, fp=None):
1462 def checkinlinesize(self, tr, fp=None):
1462 """Check if the revlog is too big for inline and convert if so.
1463 """Check if the revlog is too big for inline and convert if so.
1463
1464
1464 This should be called after revisions are added to the revlog. If the
1465 This should be called after revisions are added to the revlog. If the
1465 revlog has grown too large to be an inline revlog, it will convert it
1466 revlog has grown too large to be an inline revlog, it will convert it
1466 to use multiple index and data files.
1467 to use multiple index and data files.
1467 """
1468 """
1468 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1469 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1469 return
1470 return
1470
1471
1471 trinfo = tr.find(self.indexfile)
1472 trinfo = tr.find(self.indexfile)
1472 if trinfo is None:
1473 if trinfo is None:
1473 raise RevlogError(_("%s not found in the transaction")
1474 raise RevlogError(_("%s not found in the transaction")
1474 % self.indexfile)
1475 % self.indexfile)
1475
1476
1476 trindex = trinfo[2]
1477 trindex = trinfo[2]
1477 if trindex is not None:
1478 if trindex is not None:
1478 dataoff = self.start(trindex)
1479 dataoff = self.start(trindex)
1479 else:
1480 else:
1480 # revlog was stripped at start of transaction, use all leftover data
1481 # revlog was stripped at start of transaction, use all leftover data
1481 trindex = len(self) - 1
1482 trindex = len(self) - 1
1482 dataoff = self.end(-2)
1483 dataoff = self.end(-2)
1483
1484
1484 tr.add(self.datafile, dataoff)
1485 tr.add(self.datafile, dataoff)
1485
1486
1486 if fp:
1487 if fp:
1487 fp.flush()
1488 fp.flush()
1488 fp.close()
1489 fp.close()
1489
1490
1490 df = self.opener(self.datafile, 'w')
1491 df = self.opener(self.datafile, 'w')
1491 try:
1492 try:
1492 for r in self:
1493 for r in self:
1493 df.write(self._getsegmentforrevs(r, r)[1])
1494 df.write(self._getsegmentforrevs(r, r)[1])
1494 finally:
1495 finally:
1495 df.close()
1496 df.close()
1496
1497
1497 fp = self.opener(self.indexfile, 'w', atomictemp=True,
1498 fp = self.opener(self.indexfile, 'w', atomictemp=True,
1498 checkambig=self._checkambig)
1499 checkambig=self._checkambig)
1499 self.version &= ~FLAG_INLINE_DATA
1500 self.version &= ~FLAG_INLINE_DATA
1500 self._inline = False
1501 self._inline = False
1501 for i in self:
1502 for i in self:
1502 e = self._io.packentry(self.index[i], self.node, self.version, i)
1503 e = self._io.packentry(self.index[i], self.node, self.version, i)
1503 fp.write(e)
1504 fp.write(e)
1504
1505
1505 # if we don't call close, the temp file will never replace the
1506 # if we don't call close, the temp file will never replace the
1506 # real index
1507 # real index
1507 fp.close()
1508 fp.close()
1508
1509
1509 tr.replace(self.indexfile, trindex * self._io.size)
1510 tr.replace(self.indexfile, trindex * self._io.size)
1510 self._chunkclear()
1511 self._chunkclear()
1511
1512
1512 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1513 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1513 node=None, flags=REVIDX_DEFAULT_FLAGS):
1514 node=None, flags=REVIDX_DEFAULT_FLAGS):
1514 """add a revision to the log
1515 """add a revision to the log
1515
1516
1516 text - the revision data to add
1517 text - the revision data to add
1517 transaction - the transaction object used for rollback
1518 transaction - the transaction object used for rollback
1518 link - the linkrev data to add
1519 link - the linkrev data to add
1519 p1, p2 - the parent nodeids of the revision
1520 p1, p2 - the parent nodeids of the revision
1520 cachedelta - an optional precomputed delta
1521 cachedelta - an optional precomputed delta
1521 node - nodeid of revision; typically node is not specified, and it is
1522 node - nodeid of revision; typically node is not specified, and it is
1522 computed by default as hash(text, p1, p2), however subclasses might
1523 computed by default as hash(text, p1, p2), however subclasses might
1523 use different hashing method (and override checkhash() in such case)
1524 use different hashing method (and override checkhash() in such case)
1524 flags - the known flags to set on the revision
1525 flags - the known flags to set on the revision
1525 """
1526 """
1526 if link == nullrev:
1527 if link == nullrev:
1527 raise RevlogError(_("attempted to add linkrev -1 to %s")
1528 raise RevlogError(_("attempted to add linkrev -1 to %s")
1528 % self.indexfile)
1529 % self.indexfile)
1529
1530
1530 if flags:
1531 if flags:
1531 node = node or self.hash(text, p1, p2)
1532 node = node or self.hash(text, p1, p2)
1532
1533
1533 rawtext, validatehash = self._processflags(text, flags, 'write')
1534 rawtext, validatehash = self._processflags(text, flags, 'write')
1534
1535
1535 # If the flag processor modifies the revision data, ignore any provided
1536 # If the flag processor modifies the revision data, ignore any provided
1536 # cachedelta.
1537 # cachedelta.
1537 if rawtext != text:
1538 if rawtext != text:
1538 cachedelta = None
1539 cachedelta = None
1539
1540
1540 if len(rawtext) > _maxentrysize:
1541 if len(rawtext) > _maxentrysize:
1541 raise RevlogError(
1542 raise RevlogError(
1542 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1543 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1543 % (self.indexfile, len(rawtext)))
1544 % (self.indexfile, len(rawtext)))
1544
1545
1545 node = node or self.hash(rawtext, p1, p2)
1546 node = node or self.hash(rawtext, p1, p2)
1546 if node in self.nodemap:
1547 if node in self.nodemap:
1547 return node
1548 return node
1548
1549
1549 if validatehash:
1550 if validatehash:
1550 self.checkhash(rawtext, node, p1=p1, p2=p2)
1551 self.checkhash(rawtext, node, p1=p1, p2=p2)
1551
1552
1552 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
1553 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
1553 flags, cachedelta=cachedelta)
1554 flags, cachedelta=cachedelta)
1554
1555
1555 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
1556 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
1556 cachedelta=None):
1557 cachedelta=None):
1557 """add a raw revision with known flags, node and parents
1558 """add a raw revision with known flags, node and parents
1558 useful when reusing a revision not stored in this revlog (ex: received
1559 useful when reusing a revision not stored in this revlog (ex: received
1559 over wire, or read from an external bundle).
1560 over wire, or read from an external bundle).
1560 """
1561 """
1561 dfh = None
1562 dfh = None
1562 if not self._inline:
1563 if not self._inline:
1563 dfh = self.opener(self.datafile, "a+")
1564 dfh = self.opener(self.datafile, "a+")
1564 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1565 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1565 try:
1566 try:
1566 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1567 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1567 flags, cachedelta, ifh, dfh)
1568 flags, cachedelta, ifh, dfh)
1568 finally:
1569 finally:
1569 if dfh:
1570 if dfh:
1570 dfh.close()
1571 dfh.close()
1571 ifh.close()
1572 ifh.close()
1572
1573
1573 def compress(self, data):
1574 def compress(self, data):
1574 """Generate a possibly-compressed representation of data."""
1575 """Generate a possibly-compressed representation of data."""
1575 if not data:
1576 if not data:
1576 return '', data
1577 return '', data
1577
1578
1578 compressed = self._compressor.compress(data)
1579 compressed = self._compressor.compress(data)
1579
1580
1580 if compressed:
1581 if compressed:
1581 # The revlog compressor added the header in the returned data.
1582 # The revlog compressor added the header in the returned data.
1582 return '', compressed
1583 return '', compressed
1583
1584
1584 if data[0:1] == '\0':
1585 if data[0:1] == '\0':
1585 return '', data
1586 return '', data
1586 return 'u', data
1587 return 'u', data
1587
1588
1588 def decompress(self, data):
1589 def decompress(self, data):
1589 """Decompress a revlog chunk.
1590 """Decompress a revlog chunk.
1590
1591
1591 The chunk is expected to begin with a header identifying the
1592 The chunk is expected to begin with a header identifying the
1592 format type so it can be routed to an appropriate decompressor.
1593 format type so it can be routed to an appropriate decompressor.
1593 """
1594 """
1594 if not data:
1595 if not data:
1595 return data
1596 return data
1596
1597
1597 # Revlogs are read much more frequently than they are written and many
1598 # Revlogs are read much more frequently than they are written and many
1598 # chunks only take microseconds to decompress, so performance is
1599 # chunks only take microseconds to decompress, so performance is
1599 # important here.
1600 # important here.
1600 #
1601 #
1601 # We can make a few assumptions about revlogs:
1602 # We can make a few assumptions about revlogs:
1602 #
1603 #
1603 # 1) the majority of chunks will be compressed (as opposed to inline
1604 # 1) the majority of chunks will be compressed (as opposed to inline
1604 # raw data).
1605 # raw data).
1605 # 2) decompressing *any* data will likely by at least 10x slower than
1606 # 2) decompressing *any* data will likely by at least 10x slower than
1606 # returning raw inline data.
1607 # returning raw inline data.
1607 # 3) we want to prioritize common and officially supported compression
1608 # 3) we want to prioritize common and officially supported compression
1608 # engines
1609 # engines
1609 #
1610 #
1610 # It follows that we want to optimize for "decompress compressed data
1611 # It follows that we want to optimize for "decompress compressed data
1611 # when encoded with common and officially supported compression engines"
1612 # when encoded with common and officially supported compression engines"
1612 # case over "raw data" and "data encoded by less common or non-official
1613 # case over "raw data" and "data encoded by less common or non-official
1613 # compression engines." That is why we have the inline lookup first
1614 # compression engines." That is why we have the inline lookup first
1614 # followed by the compengines lookup.
1615 # followed by the compengines lookup.
1615 #
1616 #
1616 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1617 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1617 # compressed chunks. And this matters for changelog and manifest reads.
1618 # compressed chunks. And this matters for changelog and manifest reads.
1618 t = data[0:1]
1619 t = data[0:1]
1619
1620
1620 if t == 'x':
1621 if t == 'x':
1621 try:
1622 try:
1622 return _zlibdecompress(data)
1623 return _zlibdecompress(data)
1623 except zlib.error as e:
1624 except zlib.error as e:
1624 raise RevlogError(_('revlog decompress error: %s') % str(e))
1625 raise RevlogError(_('revlog decompress error: %s') % str(e))
1625 # '\0' is more common than 'u' so it goes first.
1626 # '\0' is more common than 'u' so it goes first.
1626 elif t == '\0':
1627 elif t == '\0':
1627 return data
1628 return data
1628 elif t == 'u':
1629 elif t == 'u':
1629 return util.buffer(data, 1)
1630 return util.buffer(data, 1)
1630
1631
1631 try:
1632 try:
1632 compressor = self._decompressors[t]
1633 compressor = self._decompressors[t]
1633 except KeyError:
1634 except KeyError:
1634 try:
1635 try:
1635 engine = util.compengines.forrevlogheader(t)
1636 engine = util.compengines.forrevlogheader(t)
1636 compressor = engine.revlogcompressor()
1637 compressor = engine.revlogcompressor()
1637 self._decompressors[t] = compressor
1638 self._decompressors[t] = compressor
1638 except KeyError:
1639 except KeyError:
1639 raise RevlogError(_('unknown compression type %r') % t)
1640 raise RevlogError(_('unknown compression type %r') % t)
1640
1641
1641 return compressor.decompress(data)
1642 return compressor.decompress(data)
1642
1643
1643 def _isgooddelta(self, d, textlen):
1644 def _isgooddelta(self, d, textlen):
1644 """Returns True if the given delta is good. Good means that it is within
1645 """Returns True if the given delta is good. Good means that it is within
1645 the disk span, disk size, and chain length bounds that we know to be
1646 the disk span, disk size, and chain length bounds that we know to be
1646 performant."""
1647 performant."""
1647 if d is None:
1648 if d is None:
1648 return False
1649 return False
1649
1650
1650 # - 'dist' is the distance from the base revision -- bounding it limits
1651 # - 'dist' is the distance from the base revision -- bounding it limits
1651 # the amount of I/O we need to do.
1652 # the amount of I/O we need to do.
1652 # - 'compresseddeltalen' is the sum of the total size of deltas we need
1653 # - 'compresseddeltalen' is the sum of the total size of deltas we need
1653 # to apply -- bounding it limits the amount of CPU we consume.
1654 # to apply -- bounding it limits the amount of CPU we consume.
1654 dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
1655 dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
1655 if (dist > textlen * 4 or l > textlen or
1656 if (dist > textlen * 4 or l > textlen or
1656 compresseddeltalen > textlen * 2 or
1657 compresseddeltalen > textlen * 2 or
1657 (self._maxchainlen and chainlen > self._maxchainlen)):
1658 (self._maxchainlen and chainlen > self._maxchainlen)):
1658 return False
1659 return False
1659
1660
1660 return True
1661 return True
1661
1662
1662 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
1663 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
1663 cachedelta, ifh, dfh, alwayscache=False):
1664 cachedelta, ifh, dfh, alwayscache=False):
1664 """internal function to add revisions to the log
1665 """internal function to add revisions to the log
1665
1666
1666 see addrevision for argument descriptions.
1667 see addrevision for argument descriptions.
1667
1668
1668 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
1669 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
1669
1670
1670 invariants:
1671 invariants:
1671 - rawtext is optional (can be None); if not set, cachedelta must be set.
1672 - rawtext is optional (can be None); if not set, cachedelta must be set.
1672 if both are set, they must correspond to each other.
1673 if both are set, they must correspond to each other.
1673 """
1674 """
1674 btext = [rawtext]
1675 btext = [rawtext]
1675 def buildtext():
1676 def buildtext():
1676 if btext[0] is not None:
1677 if btext[0] is not None:
1677 return btext[0]
1678 return btext[0]
1678 baserev = cachedelta[0]
1679 baserev = cachedelta[0]
1679 delta = cachedelta[1]
1680 delta = cachedelta[1]
1680 # special case deltas which replace entire base; no need to decode
1681 # special case deltas which replace entire base; no need to decode
1681 # base revision. this neatly avoids censored bases, which throw when
1682 # base revision. this neatly avoids censored bases, which throw when
1682 # they're decoded.
1683 # they're decoded.
1683 hlen = struct.calcsize(">lll")
1684 hlen = struct.calcsize(">lll")
1684 if delta[:hlen] == mdiff.replacediffheader(self.rawsize(baserev),
1685 if delta[:hlen] == mdiff.replacediffheader(self.rawsize(baserev),
1685 len(delta) - hlen):
1686 len(delta) - hlen):
1686 btext[0] = delta[hlen:]
1687 btext[0] = delta[hlen:]
1687 else:
1688 else:
1688 if self._inline:
1689 if self._inline:
1689 fh = ifh
1690 fh = ifh
1690 else:
1691 else:
1691 fh = dfh
1692 fh = dfh
1692 basetext = self.revision(baserev, _df=fh, raw=True)
1693 basetext = self.revision(baserev, _df=fh, raw=True)
1693 btext[0] = mdiff.patch(basetext, delta)
1694 btext[0] = mdiff.patch(basetext, delta)
1694
1695
1695 try:
1696 try:
1696 res = self._processflags(btext[0], flags, 'read', raw=True)
1697 res = self._processflags(btext[0], flags, 'read', raw=True)
1697 btext[0], validatehash = res
1698 btext[0], validatehash = res
1698 if validatehash:
1699 if validatehash:
1699 self.checkhash(btext[0], node, p1=p1, p2=p2)
1700 self.checkhash(btext[0], node, p1=p1, p2=p2)
1700 if flags & REVIDX_ISCENSORED:
1701 if flags & REVIDX_ISCENSORED:
1701 raise RevlogError(_('node %s is not censored') % node)
1702 raise RevlogError(_('node %s is not censored') % node)
1702 except CensoredNodeError:
1703 except CensoredNodeError:
1703 # must pass the censored index flag to add censored revisions
1704 # must pass the censored index flag to add censored revisions
1704 if not flags & REVIDX_ISCENSORED:
1705 if not flags & REVIDX_ISCENSORED:
1705 raise
1706 raise
1706 return btext[0]
1707 return btext[0]
1707
1708
1708 def builddelta(rev):
1709 def builddelta(rev):
1709 # can we use the cached delta?
1710 # can we use the cached delta?
1710 if cachedelta and cachedelta[0] == rev:
1711 if cachedelta and cachedelta[0] == rev:
1711 delta = cachedelta[1]
1712 delta = cachedelta[1]
1712 else:
1713 else:
1713 t = buildtext()
1714 t = buildtext()
1714 if self.iscensored(rev):
1715 if self.iscensored(rev):
1715 # deltas based on a censored revision must replace the
1716 # deltas based on a censored revision must replace the
1716 # full content in one patch, so delta works everywhere
1717 # full content in one patch, so delta works everywhere
1717 header = mdiff.replacediffheader(self.rawsize(rev), len(t))
1718 header = mdiff.replacediffheader(self.rawsize(rev), len(t))
1718 delta = header + t
1719 delta = header + t
1719 else:
1720 else:
1720 if self._inline:
1721 if self._inline:
1721 fh = ifh
1722 fh = ifh
1722 else:
1723 else:
1723 fh = dfh
1724 fh = dfh
1724 ptext = self.revision(rev, _df=fh, raw=True)
1725 ptext = self.revision(rev, _df=fh, raw=True)
1725 delta = mdiff.textdiff(ptext, t)
1726 delta = mdiff.textdiff(ptext, t)
1726 header, data = self.compress(delta)
1727 header, data = self.compress(delta)
1727 deltalen = len(header) + len(data)
1728 deltalen = len(header) + len(data)
1728 chainbase = self.chainbase(rev)
1729 chainbase = self.chainbase(rev)
1729 dist = deltalen + offset - self.start(chainbase)
1730 dist = deltalen + offset - self.start(chainbase)
1730 if self._generaldelta:
1731 if self._generaldelta:
1731 base = rev
1732 base = rev
1732 else:
1733 else:
1733 base = chainbase
1734 base = chainbase
1734 chainlen, compresseddeltalen = self._chaininfo(rev)
1735 chainlen, compresseddeltalen = self._chaininfo(rev)
1735 chainlen += 1
1736 chainlen += 1
1736 compresseddeltalen += deltalen
1737 compresseddeltalen += deltalen
1737 return (dist, deltalen, (header, data), base,
1738 return (dist, deltalen, (header, data), base,
1738 chainbase, chainlen, compresseddeltalen)
1739 chainbase, chainlen, compresseddeltalen)
1739
1740
1740 curr = len(self)
1741 curr = len(self)
1741 prev = curr - 1
1742 prev = curr - 1
1742 offset = self.end(prev)
1743 offset = self.end(prev)
1743 delta = None
1744 delta = None
1744 p1r, p2r = self.rev(p1), self.rev(p2)
1745 p1r, p2r = self.rev(p1), self.rev(p2)
1745
1746
1746 # full versions are inserted when the needed deltas
1747 # full versions are inserted when the needed deltas
1747 # become comparable to the uncompressed text
1748 # become comparable to the uncompressed text
1748 if rawtext is None:
1749 if rawtext is None:
1749 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1750 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1750 cachedelta[1])
1751 cachedelta[1])
1751 else:
1752 else:
1752 textlen = len(rawtext)
1753 textlen = len(rawtext)
1753
1754
1754 # should we try to build a delta?
1755 # should we try to build a delta?
1755 if prev != nullrev and self.storedeltachains:
1756 if prev != nullrev and self.storedeltachains:
1756 tested = set()
1757 tested = set()
1757 # This condition is true most of the time when processing
1758 # This condition is true most of the time when processing
1758 # changegroup data into a generaldelta repo. The only time it
1759 # changegroup data into a generaldelta repo. The only time it
1759 # isn't true is if this is the first revision in a delta chain
1760 # isn't true is if this is the first revision in a delta chain
1760 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
1761 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
1761 if cachedelta and self._generaldelta and self._lazydeltabase:
1762 if cachedelta and self._generaldelta and self._lazydeltabase:
1762 # Assume what we received from the server is a good choice
1763 # Assume what we received from the server is a good choice
1763 # build delta will reuse the cache
1764 # build delta will reuse the cache
1764 candidatedelta = builddelta(cachedelta[0])
1765 candidatedelta = builddelta(cachedelta[0])
1765 tested.add(cachedelta[0])
1766 tested.add(cachedelta[0])
1766 if self._isgooddelta(candidatedelta, textlen):
1767 if self._isgooddelta(candidatedelta, textlen):
1767 delta = candidatedelta
1768 delta = candidatedelta
1768 if delta is None and self._generaldelta:
1769 if delta is None and self._generaldelta:
1769 # exclude already lazy tested base if any
1770 # exclude already lazy tested base if any
1770 parents = [p for p in (p1r, p2r)
1771 parents = [p for p in (p1r, p2r)
1771 if p != nullrev and p not in tested]
1772 if p != nullrev and p not in tested]
1772 if parents and not self._aggressivemergedeltas:
1773 if parents and not self._aggressivemergedeltas:
1773 # Pick whichever parent is closer to us (to minimize the
1774 # Pick whichever parent is closer to us (to minimize the
1774 # chance of having to build a fulltext).
1775 # chance of having to build a fulltext).
1775 parents = [max(parents)]
1776 parents = [max(parents)]
1776 tested.update(parents)
1777 tested.update(parents)
1777 pdeltas = []
1778 pdeltas = []
1778 for p in parents:
1779 for p in parents:
1779 pd = builddelta(p)
1780 pd = builddelta(p)
1780 if self._isgooddelta(pd, textlen):
1781 if self._isgooddelta(pd, textlen):
1781 pdeltas.append(pd)
1782 pdeltas.append(pd)
1782 if pdeltas:
1783 if pdeltas:
1783 delta = min(pdeltas, key=lambda x: x[1])
1784 delta = min(pdeltas, key=lambda x: x[1])
1784 if delta is None and prev not in tested:
1785 if delta is None and prev not in tested:
1785 # other approach failed try against prev to hopefully save us a
1786 # other approach failed try against prev to hopefully save us a
1786 # fulltext.
1787 # fulltext.
1787 candidatedelta = builddelta(prev)
1788 candidatedelta = builddelta(prev)
1788 if self._isgooddelta(candidatedelta, textlen):
1789 if self._isgooddelta(candidatedelta, textlen):
1789 delta = candidatedelta
1790 delta = candidatedelta
1790 if delta is not None:
1791 if delta is not None:
1791 dist, l, data, base, chainbase, chainlen, compresseddeltalen = delta
1792 dist, l, data, base, chainbase, chainlen, compresseddeltalen = delta
1792 else:
1793 else:
1793 rawtext = buildtext()
1794 rawtext = buildtext()
1794 data = self.compress(rawtext)
1795 data = self.compress(rawtext)
1795 l = len(data[1]) + len(data[0])
1796 l = len(data[1]) + len(data[0])
1796 base = chainbase = curr
1797 base = chainbase = curr
1797
1798
1798 e = (offset_type(offset, flags), l, textlen,
1799 e = (offset_type(offset, flags), l, textlen,
1799 base, link, p1r, p2r, node)
1800 base, link, p1r, p2r, node)
1800 self.index.insert(-1, e)
1801 self.index.insert(-1, e)
1801 self.nodemap[node] = curr
1802 self.nodemap[node] = curr
1802
1803
1803 entry = self._io.packentry(e, self.node, self.version, curr)
1804 entry = self._io.packentry(e, self.node, self.version, curr)
1804 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1805 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1805
1806
1806 if alwayscache and rawtext is None:
1807 if alwayscache and rawtext is None:
1807 rawtext = buildtext()
1808 rawtext = buildtext()
1808
1809
1809 if type(rawtext) == str: # only accept immutable objects
1810 if type(rawtext) == str: # only accept immutable objects
1810 self._cache = (node, curr, rawtext)
1811 self._cache = (node, curr, rawtext)
1811 self._chainbasecache[curr] = chainbase
1812 self._chainbasecache[curr] = chainbase
1812 return node
1813 return node
1813
1814
1814 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1815 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1815 # Files opened in a+ mode have inconsistent behavior on various
1816 # Files opened in a+ mode have inconsistent behavior on various
1816 # platforms. Windows requires that a file positioning call be made
1817 # platforms. Windows requires that a file positioning call be made
1817 # when the file handle transitions between reads and writes. See
1818 # when the file handle transitions between reads and writes. See
1818 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
1819 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
1819 # platforms, Python or the platform itself can be buggy. Some versions
1820 # platforms, Python or the platform itself can be buggy. Some versions
1820 # of Solaris have been observed to not append at the end of the file
1821 # of Solaris have been observed to not append at the end of the file
1821 # if the file was seeked to before the end. See issue4943 for more.
1822 # if the file was seeked to before the end. See issue4943 for more.
1822 #
1823 #
1823 # We work around this issue by inserting a seek() before writing.
1824 # We work around this issue by inserting a seek() before writing.
1824 # Note: This is likely not necessary on Python 3.
1825 # Note: This is likely not necessary on Python 3.
1825 ifh.seek(0, os.SEEK_END)
1826 ifh.seek(0, os.SEEK_END)
1826 if dfh:
1827 if dfh:
1827 dfh.seek(0, os.SEEK_END)
1828 dfh.seek(0, os.SEEK_END)
1828
1829
1829 curr = len(self) - 1
1830 curr = len(self) - 1
1830 if not self._inline:
1831 if not self._inline:
1831 transaction.add(self.datafile, offset)
1832 transaction.add(self.datafile, offset)
1832 transaction.add(self.indexfile, curr * len(entry))
1833 transaction.add(self.indexfile, curr * len(entry))
1833 if data[0]:
1834 if data[0]:
1834 dfh.write(data[0])
1835 dfh.write(data[0])
1835 dfh.write(data[1])
1836 dfh.write(data[1])
1836 ifh.write(entry)
1837 ifh.write(entry)
1837 else:
1838 else:
1838 offset += curr * self._io.size
1839 offset += curr * self._io.size
1839 transaction.add(self.indexfile, offset, curr)
1840 transaction.add(self.indexfile, offset, curr)
1840 ifh.write(entry)
1841 ifh.write(entry)
1841 ifh.write(data[0])
1842 ifh.write(data[0])
1842 ifh.write(data[1])
1843 ifh.write(data[1])
1843 self.checkinlinesize(transaction, ifh)
1844 self.checkinlinesize(transaction, ifh)
1844
1845
1845 def addgroup(self, cg, linkmapper, transaction, addrevisioncb=None):
1846 def addgroup(self, cg, linkmapper, transaction, addrevisioncb=None):
1846 """
1847 """
1847 add a delta group
1848 add a delta group
1848
1849
1849 given a set of deltas, add them to the revision log. the
1850 given a set of deltas, add them to the revision log. the
1850 first delta is against its parent, which should be in our
1851 first delta is against its parent, which should be in our
1851 log, the rest are against the previous delta.
1852 log, the rest are against the previous delta.
1852
1853
1853 If ``addrevisioncb`` is defined, it will be called with arguments of
1854 If ``addrevisioncb`` is defined, it will be called with arguments of
1854 this revlog and the node that was added.
1855 this revlog and the node that was added.
1855 """
1856 """
1856
1857
1857 nodes = []
1858 nodes = []
1858
1859
1859 r = len(self)
1860 r = len(self)
1860 end = 0
1861 end = 0
1861 if r:
1862 if r:
1862 end = self.end(r - 1)
1863 end = self.end(r - 1)
1863 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1864 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1864 isize = r * self._io.size
1865 isize = r * self._io.size
1865 if self._inline:
1866 if self._inline:
1866 transaction.add(self.indexfile, end + isize, r)
1867 transaction.add(self.indexfile, end + isize, r)
1867 dfh = None
1868 dfh = None
1868 else:
1869 else:
1869 transaction.add(self.indexfile, isize, r)
1870 transaction.add(self.indexfile, isize, r)
1870 transaction.add(self.datafile, end)
1871 transaction.add(self.datafile, end)
1871 dfh = self.opener(self.datafile, "a+")
1872 dfh = self.opener(self.datafile, "a+")
1872 def flush():
1873 def flush():
1873 if dfh:
1874 if dfh:
1874 dfh.flush()
1875 dfh.flush()
1875 ifh.flush()
1876 ifh.flush()
1876 try:
1877 try:
1877 # loop through our set of deltas
1878 # loop through our set of deltas
1878 chain = None
1879 chain = None
1879 for chunkdata in iter(lambda: cg.deltachunk(chain), {}):
1880 for chunkdata in iter(lambda: cg.deltachunk(chain), {}):
1880 node = chunkdata['node']
1881 node = chunkdata['node']
1881 p1 = chunkdata['p1']
1882 p1 = chunkdata['p1']
1882 p2 = chunkdata['p2']
1883 p2 = chunkdata['p2']
1883 cs = chunkdata['cs']
1884 cs = chunkdata['cs']
1884 deltabase = chunkdata['deltabase']
1885 deltabase = chunkdata['deltabase']
1885 delta = chunkdata['delta']
1886 delta = chunkdata['delta']
1886 flags = chunkdata['flags'] or REVIDX_DEFAULT_FLAGS
1887 flags = chunkdata['flags'] or REVIDX_DEFAULT_FLAGS
1887
1888
1888 nodes.append(node)
1889 nodes.append(node)
1889
1890
1890 link = linkmapper(cs)
1891 link = linkmapper(cs)
1891 if node in self.nodemap:
1892 if node in self.nodemap:
1892 # this can happen if two branches make the same change
1893 # this can happen if two branches make the same change
1893 chain = node
1894 chain = node
1894 continue
1895 continue
1895
1896
1896 for p in (p1, p2):
1897 for p in (p1, p2):
1897 if p not in self.nodemap:
1898 if p not in self.nodemap:
1898 raise LookupError(p, self.indexfile,
1899 raise LookupError(p, self.indexfile,
1899 _('unknown parent'))
1900 _('unknown parent'))
1900
1901
1901 if deltabase not in self.nodemap:
1902 if deltabase not in self.nodemap:
1902 raise LookupError(deltabase, self.indexfile,
1903 raise LookupError(deltabase, self.indexfile,
1903 _('unknown delta base'))
1904 _('unknown delta base'))
1904
1905
1905 baserev = self.rev(deltabase)
1906 baserev = self.rev(deltabase)
1906
1907
1907 if baserev != nullrev and self.iscensored(baserev):
1908 if baserev != nullrev and self.iscensored(baserev):
1908 # if base is censored, delta must be full replacement in a
1909 # if base is censored, delta must be full replacement in a
1909 # single patch operation
1910 # single patch operation
1910 hlen = struct.calcsize(">lll")
1911 hlen = struct.calcsize(">lll")
1911 oldlen = self.rawsize(baserev)
1912 oldlen = self.rawsize(baserev)
1912 newlen = len(delta) - hlen
1913 newlen = len(delta) - hlen
1913 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
1914 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
1914 raise error.CensoredBaseError(self.indexfile,
1915 raise error.CensoredBaseError(self.indexfile,
1915 self.node(baserev))
1916 self.node(baserev))
1916
1917
1917 if not flags and self._peek_iscensored(baserev, delta, flush):
1918 if not flags and self._peek_iscensored(baserev, delta, flush):
1918 flags |= REVIDX_ISCENSORED
1919 flags |= REVIDX_ISCENSORED
1919
1920
1920 # We assume consumers of addrevisioncb will want to retrieve
1921 # We assume consumers of addrevisioncb will want to retrieve
1921 # the added revision, which will require a call to
1922 # the added revision, which will require a call to
1922 # revision(). revision() will fast path if there is a cache
1923 # revision(). revision() will fast path if there is a cache
1923 # hit. So, we tell _addrevision() to always cache in this case.
1924 # hit. So, we tell _addrevision() to always cache in this case.
1924 # We're only using addgroup() in the context of changegroup
1925 # We're only using addgroup() in the context of changegroup
1925 # generation so the revision data can always be handled as raw
1926 # generation so the revision data can always be handled as raw
1926 # by the flagprocessor.
1927 # by the flagprocessor.
1927 chain = self._addrevision(node, None, transaction, link,
1928 chain = self._addrevision(node, None, transaction, link,
1928 p1, p2, flags, (baserev, delta),
1929 p1, p2, flags, (baserev, delta),
1929 ifh, dfh,
1930 ifh, dfh,
1930 alwayscache=bool(addrevisioncb))
1931 alwayscache=bool(addrevisioncb))
1931
1932
1932 if addrevisioncb:
1933 if addrevisioncb:
1933 addrevisioncb(self, chain)
1934 addrevisioncb(self, chain)
1934
1935
1935 if not dfh and not self._inline:
1936 if not dfh and not self._inline:
1936 # addrevision switched from inline to conventional
1937 # addrevision switched from inline to conventional
1937 # reopen the index
1938 # reopen the index
1938 ifh.close()
1939 ifh.close()
1939 dfh = self.opener(self.datafile, "a+")
1940 dfh = self.opener(self.datafile, "a+")
1940 ifh = self.opener(self.indexfile, "a+",
1941 ifh = self.opener(self.indexfile, "a+",
1941 checkambig=self._checkambig)
1942 checkambig=self._checkambig)
1942 finally:
1943 finally:
1943 if dfh:
1944 if dfh:
1944 dfh.close()
1945 dfh.close()
1945 ifh.close()
1946 ifh.close()
1946
1947
1947 return nodes
1948 return nodes
1948
1949
1949 def iscensored(self, rev):
1950 def iscensored(self, rev):
1950 """Check if a file revision is censored."""
1951 """Check if a file revision is censored."""
1951 return False
1952 return False
1952
1953
1953 def _peek_iscensored(self, baserev, delta, flush):
1954 def _peek_iscensored(self, baserev, delta, flush):
1954 """Quickly check if a delta produces a censored revision."""
1955 """Quickly check if a delta produces a censored revision."""
1955 return False
1956 return False
1956
1957
1957 def getstrippoint(self, minlink):
1958 def getstrippoint(self, minlink):
1958 """find the minimum rev that must be stripped to strip the linkrev
1959 """find the minimum rev that must be stripped to strip the linkrev
1959
1960
1960 Returns a tuple containing the minimum rev and a set of all revs that
1961 Returns a tuple containing the minimum rev and a set of all revs that
1961 have linkrevs that will be broken by this strip.
1962 have linkrevs that will be broken by this strip.
1962 """
1963 """
1963 brokenrevs = set()
1964 brokenrevs = set()
1964 strippoint = len(self)
1965 strippoint = len(self)
1965
1966
1966 heads = {}
1967 heads = {}
1967 futurelargelinkrevs = set()
1968 futurelargelinkrevs = set()
1968 for head in self.headrevs():
1969 for head in self.headrevs():
1969 headlinkrev = self.linkrev(head)
1970 headlinkrev = self.linkrev(head)
1970 heads[head] = headlinkrev
1971 heads[head] = headlinkrev
1971 if headlinkrev >= minlink:
1972 if headlinkrev >= minlink:
1972 futurelargelinkrevs.add(headlinkrev)
1973 futurelargelinkrevs.add(headlinkrev)
1973
1974
1974 # This algorithm involves walking down the rev graph, starting at the
1975 # This algorithm involves walking down the rev graph, starting at the
1975 # heads. Since the revs are topologically sorted according to linkrev,
1976 # heads. Since the revs are topologically sorted according to linkrev,
1976 # once all head linkrevs are below the minlink, we know there are
1977 # once all head linkrevs are below the minlink, we know there are
1977 # no more revs that could have a linkrev greater than minlink.
1978 # no more revs that could have a linkrev greater than minlink.
1978 # So we can stop walking.
1979 # So we can stop walking.
1979 while futurelargelinkrevs:
1980 while futurelargelinkrevs:
1980 strippoint -= 1
1981 strippoint -= 1
1981 linkrev = heads.pop(strippoint)
1982 linkrev = heads.pop(strippoint)
1982
1983
1983 if linkrev < minlink:
1984 if linkrev < minlink:
1984 brokenrevs.add(strippoint)
1985 brokenrevs.add(strippoint)
1985 else:
1986 else:
1986 futurelargelinkrevs.remove(linkrev)
1987 futurelargelinkrevs.remove(linkrev)
1987
1988
1988 for p in self.parentrevs(strippoint):
1989 for p in self.parentrevs(strippoint):
1989 if p != nullrev:
1990 if p != nullrev:
1990 plinkrev = self.linkrev(p)
1991 plinkrev = self.linkrev(p)
1991 heads[p] = plinkrev
1992 heads[p] = plinkrev
1992 if plinkrev >= minlink:
1993 if plinkrev >= minlink:
1993 futurelargelinkrevs.add(plinkrev)
1994 futurelargelinkrevs.add(plinkrev)
1994
1995
1995 return strippoint, brokenrevs
1996 return strippoint, brokenrevs
1996
1997
1997 def strip(self, minlink, transaction):
1998 def strip(self, minlink, transaction):
1998 """truncate the revlog on the first revision with a linkrev >= minlink
1999 """truncate the revlog on the first revision with a linkrev >= minlink
1999
2000
2000 This function is called when we're stripping revision minlink and
2001 This function is called when we're stripping revision minlink and
2001 its descendants from the repository.
2002 its descendants from the repository.
2002
2003
2003 We have to remove all revisions with linkrev >= minlink, because
2004 We have to remove all revisions with linkrev >= minlink, because
2004 the equivalent changelog revisions will be renumbered after the
2005 the equivalent changelog revisions will be renumbered after the
2005 strip.
2006 strip.
2006
2007
2007 So we truncate the revlog on the first of these revisions, and
2008 So we truncate the revlog on the first of these revisions, and
2008 trust that the caller has saved the revisions that shouldn't be
2009 trust that the caller has saved the revisions that shouldn't be
2009 removed and that it'll re-add them after this truncation.
2010 removed and that it'll re-add them after this truncation.
2010 """
2011 """
2011 if len(self) == 0:
2012 if len(self) == 0:
2012 return
2013 return
2013
2014
2014 rev, _ = self.getstrippoint(minlink)
2015 rev, _ = self.getstrippoint(minlink)
2015 if rev == len(self):
2016 if rev == len(self):
2016 return
2017 return
2017
2018
2018 # first truncate the files on disk
2019 # first truncate the files on disk
2019 end = self.start(rev)
2020 end = self.start(rev)
2020 if not self._inline:
2021 if not self._inline:
2021 transaction.add(self.datafile, end)
2022 transaction.add(self.datafile, end)
2022 end = rev * self._io.size
2023 end = rev * self._io.size
2023 else:
2024 else:
2024 end += rev * self._io.size
2025 end += rev * self._io.size
2025
2026
2026 transaction.add(self.indexfile, end)
2027 transaction.add(self.indexfile, end)
2027
2028
2028 # then reset internal state in memory to forget those revisions
2029 # then reset internal state in memory to forget those revisions
2029 self._cache = None
2030 self._cache = None
2030 self._chaininfocache = {}
2031 self._chaininfocache = {}
2031 self._chunkclear()
2032 self._chunkclear()
2032 for x in xrange(rev, len(self)):
2033 for x in xrange(rev, len(self)):
2033 del self.nodemap[self.node(x)]
2034 del self.nodemap[self.node(x)]
2034
2035
2035 del self.index[rev:-1]
2036 del self.index[rev:-1]
2036
2037
2037 def checksize(self):
2038 def checksize(self):
2038 expected = 0
2039 expected = 0
2039 if len(self):
2040 if len(self):
2040 expected = max(0, self.end(len(self) - 1))
2041 expected = max(0, self.end(len(self) - 1))
2041
2042
2042 try:
2043 try:
2043 f = self.opener(self.datafile)
2044 f = self.opener(self.datafile)
2044 f.seek(0, 2)
2045 f.seek(0, 2)
2045 actual = f.tell()
2046 actual = f.tell()
2046 f.close()
2047 f.close()
2047 dd = actual - expected
2048 dd = actual - expected
2048 except IOError as inst:
2049 except IOError as inst:
2049 if inst.errno != errno.ENOENT:
2050 if inst.errno != errno.ENOENT:
2050 raise
2051 raise
2051 dd = 0
2052 dd = 0
2052
2053
2053 try:
2054 try:
2054 f = self.opener(self.indexfile)
2055 f = self.opener(self.indexfile)
2055 f.seek(0, 2)
2056 f.seek(0, 2)
2056 actual = f.tell()
2057 actual = f.tell()
2057 f.close()
2058 f.close()
2058 s = self._io.size
2059 s = self._io.size
2059 i = max(0, actual // s)
2060 i = max(0, actual // s)
2060 di = actual - (i * s)
2061 di = actual - (i * s)
2061 if self._inline:
2062 if self._inline:
2062 databytes = 0
2063 databytes = 0
2063 for r in self:
2064 for r in self:
2064 databytes += max(0, self.length(r))
2065 databytes += max(0, self.length(r))
2065 dd = 0
2066 dd = 0
2066 di = actual - len(self) * s - databytes
2067 di = actual - len(self) * s - databytes
2067 except IOError as inst:
2068 except IOError as inst:
2068 if inst.errno != errno.ENOENT:
2069 if inst.errno != errno.ENOENT:
2069 raise
2070 raise
2070 di = 0
2071 di = 0
2071
2072
2072 return (dd, di)
2073 return (dd, di)
2073
2074
2074 def files(self):
2075 def files(self):
2075 res = [self.indexfile]
2076 res = [self.indexfile]
2076 if not self._inline:
2077 if not self._inline:
2077 res.append(self.datafile)
2078 res.append(self.datafile)
2078 return res
2079 return res
2079
2080
2080 DELTAREUSEALWAYS = 'always'
2081 DELTAREUSEALWAYS = 'always'
2081 DELTAREUSESAMEREVS = 'samerevs'
2082 DELTAREUSESAMEREVS = 'samerevs'
2082 DELTAREUSENEVER = 'never'
2083 DELTAREUSENEVER = 'never'
2083
2084
2084 DELTAREUSEALL = {'always', 'samerevs', 'never'}
2085 DELTAREUSEALL = {'always', 'samerevs', 'never'}
2085
2086
2086 def clone(self, tr, destrevlog, addrevisioncb=None,
2087 def clone(self, tr, destrevlog, addrevisioncb=None,
2087 deltareuse=DELTAREUSESAMEREVS, aggressivemergedeltas=None):
2088 deltareuse=DELTAREUSESAMEREVS, aggressivemergedeltas=None):
2088 """Copy this revlog to another, possibly with format changes.
2089 """Copy this revlog to another, possibly with format changes.
2089
2090
2090 The destination revlog will contain the same revisions and nodes.
2091 The destination revlog will contain the same revisions and nodes.
2091 However, it may not be bit-for-bit identical due to e.g. delta encoding
2092 However, it may not be bit-for-bit identical due to e.g. delta encoding
2092 differences.
2093 differences.
2093
2094
2094 The ``deltareuse`` argument control how deltas from the existing revlog
2095 The ``deltareuse`` argument control how deltas from the existing revlog
2095 are preserved in the destination revlog. The argument can have the
2096 are preserved in the destination revlog. The argument can have the
2096 following values:
2097 following values:
2097
2098
2098 DELTAREUSEALWAYS
2099 DELTAREUSEALWAYS
2099 Deltas will always be reused (if possible), even if the destination
2100 Deltas will always be reused (if possible), even if the destination
2100 revlog would not select the same revisions for the delta. This is the
2101 revlog would not select the same revisions for the delta. This is the
2101 fastest mode of operation.
2102 fastest mode of operation.
2102 DELTAREUSESAMEREVS
2103 DELTAREUSESAMEREVS
2103 Deltas will be reused if the destination revlog would pick the same
2104 Deltas will be reused if the destination revlog would pick the same
2104 revisions for the delta. This mode strikes a balance between speed
2105 revisions for the delta. This mode strikes a balance between speed
2105 and optimization.
2106 and optimization.
2106 DELTAREUSENEVER
2107 DELTAREUSENEVER
2107 Deltas will never be reused. This is the slowest mode of execution.
2108 Deltas will never be reused. This is the slowest mode of execution.
2108 This mode can be used to recompute deltas (e.g. if the diff/delta
2109 This mode can be used to recompute deltas (e.g. if the diff/delta
2109 algorithm changes).
2110 algorithm changes).
2110
2111
2111 Delta computation can be slow, so the choice of delta reuse policy can
2112 Delta computation can be slow, so the choice of delta reuse policy can
2112 significantly affect run time.
2113 significantly affect run time.
2113
2114
2114 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2115 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2115 two extremes. Deltas will be reused if they are appropriate. But if the
2116 two extremes. Deltas will be reused if they are appropriate. But if the
2116 delta could choose a better revision, it will do so. This means if you
2117 delta could choose a better revision, it will do so. This means if you
2117 are converting a non-generaldelta revlog to a generaldelta revlog,
2118 are converting a non-generaldelta revlog to a generaldelta revlog,
2118 deltas will be recomputed if the delta's parent isn't a parent of the
2119 deltas will be recomputed if the delta's parent isn't a parent of the
2119 revision.
2120 revision.
2120
2121
2121 In addition to the delta policy, the ``aggressivemergedeltas`` argument
2122 In addition to the delta policy, the ``aggressivemergedeltas`` argument
2122 controls whether to compute deltas against both parents for merges.
2123 controls whether to compute deltas against both parents for merges.
2123 By default, the current default is used.
2124 By default, the current default is used.
2124 """
2125 """
2125 if deltareuse not in self.DELTAREUSEALL:
2126 if deltareuse not in self.DELTAREUSEALL:
2126 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2127 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2127
2128
2128 if len(destrevlog):
2129 if len(destrevlog):
2129 raise ValueError(_('destination revlog is not empty'))
2130 raise ValueError(_('destination revlog is not empty'))
2130
2131
2131 if getattr(self, 'filteredrevs', None):
2132 if getattr(self, 'filteredrevs', None):
2132 raise ValueError(_('source revlog has filtered revisions'))
2133 raise ValueError(_('source revlog has filtered revisions'))
2133 if getattr(destrevlog, 'filteredrevs', None):
2134 if getattr(destrevlog, 'filteredrevs', None):
2134 raise ValueError(_('destination revlog has filtered revisions'))
2135 raise ValueError(_('destination revlog has filtered revisions'))
2135
2136
2136 # lazydeltabase controls whether to reuse a cached delta, if possible.
2137 # lazydeltabase controls whether to reuse a cached delta, if possible.
2137 oldlazydeltabase = destrevlog._lazydeltabase
2138 oldlazydeltabase = destrevlog._lazydeltabase
2138 oldamd = destrevlog._aggressivemergedeltas
2139 oldamd = destrevlog._aggressivemergedeltas
2139
2140
2140 try:
2141 try:
2141 if deltareuse == self.DELTAREUSEALWAYS:
2142 if deltareuse == self.DELTAREUSEALWAYS:
2142 destrevlog._lazydeltabase = True
2143 destrevlog._lazydeltabase = True
2143 elif deltareuse == self.DELTAREUSESAMEREVS:
2144 elif deltareuse == self.DELTAREUSESAMEREVS:
2144 destrevlog._lazydeltabase = False
2145 destrevlog._lazydeltabase = False
2145
2146
2146 destrevlog._aggressivemergedeltas = aggressivemergedeltas or oldamd
2147 destrevlog._aggressivemergedeltas = aggressivemergedeltas or oldamd
2147
2148
2148 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2149 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2149 self.DELTAREUSESAMEREVS)
2150 self.DELTAREUSESAMEREVS)
2150
2151
2151 index = self.index
2152 index = self.index
2152 for rev in self:
2153 for rev in self:
2153 entry = index[rev]
2154 entry = index[rev]
2154
2155
2155 # Some classes override linkrev to take filtered revs into
2156 # Some classes override linkrev to take filtered revs into
2156 # account. Use raw entry from index.
2157 # account. Use raw entry from index.
2157 flags = entry[0] & 0xffff
2158 flags = entry[0] & 0xffff
2158 linkrev = entry[4]
2159 linkrev = entry[4]
2159 p1 = index[entry[5]][7]
2160 p1 = index[entry[5]][7]
2160 p2 = index[entry[6]][7]
2161 p2 = index[entry[6]][7]
2161 node = entry[7]
2162 node = entry[7]
2162
2163
2163 # (Possibly) reuse the delta from the revlog if allowed and
2164 # (Possibly) reuse the delta from the revlog if allowed and
2164 # the revlog chunk is a delta.
2165 # the revlog chunk is a delta.
2165 cachedelta = None
2166 cachedelta = None
2166 rawtext = None
2167 rawtext = None
2167 if populatecachedelta:
2168 if populatecachedelta:
2168 dp = self.deltaparent(rev)
2169 dp = self.deltaparent(rev)
2169 if dp != nullrev:
2170 if dp != nullrev:
2170 cachedelta = (dp, str(self._chunk(rev)))
2171 cachedelta = (dp, str(self._chunk(rev)))
2171
2172
2172 if not cachedelta:
2173 if not cachedelta:
2173 rawtext = self.revision(rev, raw=True)
2174 rawtext = self.revision(rev, raw=True)
2174
2175
2175 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2176 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2176 checkambig=False)
2177 checkambig=False)
2177 dfh = None
2178 dfh = None
2178 if not destrevlog._inline:
2179 if not destrevlog._inline:
2179 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2180 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2180 try:
2181 try:
2181 destrevlog._addrevision(node, rawtext, tr, linkrev, p1, p2,
2182 destrevlog._addrevision(node, rawtext, tr, linkrev, p1, p2,
2182 flags, cachedelta, ifh, dfh)
2183 flags, cachedelta, ifh, dfh)
2183 finally:
2184 finally:
2184 if dfh:
2185 if dfh:
2185 dfh.close()
2186 dfh.close()
2186 ifh.close()
2187 ifh.close()
2187
2188
2188 if addrevisioncb:
2189 if addrevisioncb:
2189 addrevisioncb(self, rev, node)
2190 addrevisioncb(self, rev, node)
2190 finally:
2191 finally:
2191 destrevlog._lazydeltabase = oldlazydeltabase
2192 destrevlog._lazydeltabase = oldlazydeltabase
2192 destrevlog._aggressivemergedeltas = oldamd
2193 destrevlog._aggressivemergedeltas = oldamd
General Comments 0
You need to be logged in to leave comments. Login now