##// END OF EJS Templates
revlog: skip over empty revision when looking for delta base...
Boris Feld -
r39118:8f83a953 default
parent child Browse files
Show More
@@ -1,2964 +1,2974
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 import collections
16 import collections
17 import contextlib
17 import contextlib
18 import errno
18 import errno
19 import hashlib
19 import hashlib
20 import heapq
20 import heapq
21 import os
21 import os
22 import re
22 import re
23 import struct
23 import struct
24 import zlib
24 import zlib
25
25
26 # import stuff from node for others to import from revlog
26 # import stuff from node for others to import from revlog
27 from .node import (
27 from .node import (
28 bin,
28 bin,
29 hex,
29 hex,
30 nullid,
30 nullid,
31 nullrev,
31 nullrev,
32 wdirfilenodeids,
32 wdirfilenodeids,
33 wdirhex,
33 wdirhex,
34 wdirid,
34 wdirid,
35 wdirrev,
35 wdirrev,
36 )
36 )
37 from .i18n import _
37 from .i18n import _
38 from .thirdparty import (
38 from .thirdparty import (
39 attr,
39 attr,
40 )
40 )
41 from . import (
41 from . import (
42 ancestor,
42 ancestor,
43 error,
43 error,
44 mdiff,
44 mdiff,
45 policy,
45 policy,
46 pycompat,
46 pycompat,
47 templatefilters,
47 templatefilters,
48 util,
48 util,
49 )
49 )
50 from .utils import (
50 from .utils import (
51 stringutil,
51 stringutil,
52 )
52 )
53
53
54 parsers = policy.importmod(r'parsers')
54 parsers = policy.importmod(r'parsers')
55
55
56 # Aliased for performance.
56 # Aliased for performance.
57 _zlibdecompress = zlib.decompress
57 _zlibdecompress = zlib.decompress
58
58
59 # revlog header flags
59 # revlog header flags
60 REVLOGV0 = 0
60 REVLOGV0 = 0
61 REVLOGV1 = 1
61 REVLOGV1 = 1
62 # Dummy value until file format is finalized.
62 # Dummy value until file format is finalized.
63 # Reminder: change the bounds check in revlog.__init__ when this is changed.
63 # Reminder: change the bounds check in revlog.__init__ when this is changed.
64 REVLOGV2 = 0xDEAD
64 REVLOGV2 = 0xDEAD
65 FLAG_INLINE_DATA = (1 << 16)
65 FLAG_INLINE_DATA = (1 << 16)
66 FLAG_GENERALDELTA = (1 << 17)
66 FLAG_GENERALDELTA = (1 << 17)
67 REVLOG_DEFAULT_FLAGS = FLAG_INLINE_DATA
67 REVLOG_DEFAULT_FLAGS = FLAG_INLINE_DATA
68 REVLOG_DEFAULT_FORMAT = REVLOGV1
68 REVLOG_DEFAULT_FORMAT = REVLOGV1
69 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
69 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
70 REVLOGV1_FLAGS = FLAG_INLINE_DATA | FLAG_GENERALDELTA
70 REVLOGV1_FLAGS = FLAG_INLINE_DATA | FLAG_GENERALDELTA
71 REVLOGV2_FLAGS = REVLOGV1_FLAGS
71 REVLOGV2_FLAGS = REVLOGV1_FLAGS
72
72
73 # revlog index flags
73 # revlog index flags
74 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
74 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
75 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
75 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
76 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
76 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
77 REVIDX_DEFAULT_FLAGS = 0
77 REVIDX_DEFAULT_FLAGS = 0
78 # stable order in which flags need to be processed and their processors applied
78 # stable order in which flags need to be processed and their processors applied
79 REVIDX_FLAGS_ORDER = [
79 REVIDX_FLAGS_ORDER = [
80 REVIDX_ISCENSORED,
80 REVIDX_ISCENSORED,
81 REVIDX_ELLIPSIS,
81 REVIDX_ELLIPSIS,
82 REVIDX_EXTSTORED,
82 REVIDX_EXTSTORED,
83 ]
83 ]
84 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
84 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
85 # bitmark for flags that could cause rawdata content change
85 # bitmark for flags that could cause rawdata content change
86 REVIDX_RAWTEXT_CHANGING_FLAGS = REVIDX_ISCENSORED | REVIDX_EXTSTORED
86 REVIDX_RAWTEXT_CHANGING_FLAGS = REVIDX_ISCENSORED | REVIDX_EXTSTORED
87
87
88 # max size of revlog with inline data
88 # max size of revlog with inline data
89 _maxinline = 131072
89 _maxinline = 131072
90 _chunksize = 1048576
90 _chunksize = 1048576
91
91
92 RevlogError = error.RevlogError
92 RevlogError = error.RevlogError
93 LookupError = error.LookupError
93 LookupError = error.LookupError
94 AmbiguousPrefixLookupError = error.AmbiguousPrefixLookupError
94 AmbiguousPrefixLookupError = error.AmbiguousPrefixLookupError
95 CensoredNodeError = error.CensoredNodeError
95 CensoredNodeError = error.CensoredNodeError
96 ProgrammingError = error.ProgrammingError
96 ProgrammingError = error.ProgrammingError
97
97
98 # Store flag processors (cf. 'addflagprocessor()' to register)
98 # Store flag processors (cf. 'addflagprocessor()' to register)
99 _flagprocessors = {
99 _flagprocessors = {
100 REVIDX_ISCENSORED: None,
100 REVIDX_ISCENSORED: None,
101 }
101 }
102
102
103 _mdre = re.compile('\1\n')
103 _mdre = re.compile('\1\n')
104 def parsemeta(text):
104 def parsemeta(text):
105 """return (metadatadict, metadatasize)"""
105 """return (metadatadict, metadatasize)"""
106 # text can be buffer, so we can't use .startswith or .index
106 # text can be buffer, so we can't use .startswith or .index
107 if text[:2] != '\1\n':
107 if text[:2] != '\1\n':
108 return None, None
108 return None, None
109 s = _mdre.search(text, 2).start()
109 s = _mdre.search(text, 2).start()
110 mtext = text[2:s]
110 mtext = text[2:s]
111 meta = {}
111 meta = {}
112 for l in mtext.splitlines():
112 for l in mtext.splitlines():
113 k, v = l.split(": ", 1)
113 k, v = l.split(": ", 1)
114 meta[k] = v
114 meta[k] = v
115 return meta, (s + 2)
115 return meta, (s + 2)
116
116
117 def packmeta(meta, text):
117 def packmeta(meta, text):
118 keys = sorted(meta)
118 keys = sorted(meta)
119 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
119 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
120 return "\1\n%s\1\n%s" % (metatext, text)
120 return "\1\n%s\1\n%s" % (metatext, text)
121
121
122 def _censoredtext(text):
122 def _censoredtext(text):
123 m, offs = parsemeta(text)
123 m, offs = parsemeta(text)
124 return m and "censored" in m
124 return m and "censored" in m
125
125
126 def addflagprocessor(flag, processor):
126 def addflagprocessor(flag, processor):
127 """Register a flag processor on a revision data flag.
127 """Register a flag processor on a revision data flag.
128
128
129 Invariant:
129 Invariant:
130 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER,
130 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER,
131 and REVIDX_RAWTEXT_CHANGING_FLAGS if they can alter rawtext.
131 and REVIDX_RAWTEXT_CHANGING_FLAGS if they can alter rawtext.
132 - Only one flag processor can be registered on a specific flag.
132 - Only one flag processor can be registered on a specific flag.
133 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
133 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
134 following signatures:
134 following signatures:
135 - (read) f(self, rawtext) -> text, bool
135 - (read) f(self, rawtext) -> text, bool
136 - (write) f(self, text) -> rawtext, bool
136 - (write) f(self, text) -> rawtext, bool
137 - (raw) f(self, rawtext) -> bool
137 - (raw) f(self, rawtext) -> bool
138 "text" is presented to the user. "rawtext" is stored in revlog data, not
138 "text" is presented to the user. "rawtext" is stored in revlog data, not
139 directly visible to the user.
139 directly visible to the user.
140 The boolean returned by these transforms is used to determine whether
140 The boolean returned by these transforms is used to determine whether
141 the returned text can be used for hash integrity checking. For example,
141 the returned text can be used for hash integrity checking. For example,
142 if "write" returns False, then "text" is used to generate hash. If
142 if "write" returns False, then "text" is used to generate hash. If
143 "write" returns True, that basically means "rawtext" returned by "write"
143 "write" returns True, that basically means "rawtext" returned by "write"
144 should be used to generate hash. Usually, "write" and "read" return
144 should be used to generate hash. Usually, "write" and "read" return
145 different booleans. And "raw" returns a same boolean as "write".
145 different booleans. And "raw" returns a same boolean as "write".
146
146
147 Note: The 'raw' transform is used for changegroup generation and in some
147 Note: The 'raw' transform is used for changegroup generation and in some
148 debug commands. In this case the transform only indicates whether the
148 debug commands. In this case the transform only indicates whether the
149 contents can be used for hash integrity checks.
149 contents can be used for hash integrity checks.
150 """
150 """
151 if not flag & REVIDX_KNOWN_FLAGS:
151 if not flag & REVIDX_KNOWN_FLAGS:
152 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
152 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
153 raise ProgrammingError(msg)
153 raise ProgrammingError(msg)
154 if flag not in REVIDX_FLAGS_ORDER:
154 if flag not in REVIDX_FLAGS_ORDER:
155 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
155 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
156 raise ProgrammingError(msg)
156 raise ProgrammingError(msg)
157 if flag in _flagprocessors:
157 if flag in _flagprocessors:
158 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
158 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
159 raise error.Abort(msg)
159 raise error.Abort(msg)
160 _flagprocessors[flag] = processor
160 _flagprocessors[flag] = processor
161
161
162 def getoffset(q):
162 def getoffset(q):
163 return int(q >> 16)
163 return int(q >> 16)
164
164
165 def gettype(q):
165 def gettype(q):
166 return int(q & 0xFFFF)
166 return int(q & 0xFFFF)
167
167
168 def offset_type(offset, type):
168 def offset_type(offset, type):
169 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
169 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
170 raise ValueError('unknown revlog index flags')
170 raise ValueError('unknown revlog index flags')
171 return int(int(offset) << 16 | type)
171 return int(int(offset) << 16 | type)
172
172
173 _nullhash = hashlib.sha1(nullid)
173 _nullhash = hashlib.sha1(nullid)
174
174
175 def hash(text, p1, p2):
175 def hash(text, p1, p2):
176 """generate a hash from the given text and its parent hashes
176 """generate a hash from the given text and its parent hashes
177
177
178 This hash combines both the current file contents and its history
178 This hash combines both the current file contents and its history
179 in a manner that makes it easy to distinguish nodes with the same
179 in a manner that makes it easy to distinguish nodes with the same
180 content in the revision graph.
180 content in the revision graph.
181 """
181 """
182 # As of now, if one of the parent node is null, p2 is null
182 # As of now, if one of the parent node is null, p2 is null
183 if p2 == nullid:
183 if p2 == nullid:
184 # deep copy of a hash is faster than creating one
184 # deep copy of a hash is faster than creating one
185 s = _nullhash.copy()
185 s = _nullhash.copy()
186 s.update(p1)
186 s.update(p1)
187 else:
187 else:
188 # none of the parent nodes are nullid
188 # none of the parent nodes are nullid
189 if p1 < p2:
189 if p1 < p2:
190 a = p1
190 a = p1
191 b = p2
191 b = p2
192 else:
192 else:
193 a = p2
193 a = p2
194 b = p1
194 b = p1
195 s = hashlib.sha1(a)
195 s = hashlib.sha1(a)
196 s.update(b)
196 s.update(b)
197 s.update(text)
197 s.update(text)
198 return s.digest()
198 return s.digest()
199
199
200 class _testrevlog(object):
200 class _testrevlog(object):
201 """minimalist fake revlog to use in doctests"""
201 """minimalist fake revlog to use in doctests"""
202
202
203 def __init__(self, data, density=0.5, mingap=0):
203 def __init__(self, data, density=0.5, mingap=0):
204 """data is an list of revision payload boundaries"""
204 """data is an list of revision payload boundaries"""
205 self._data = data
205 self._data = data
206 self._srdensitythreshold = density
206 self._srdensitythreshold = density
207 self._srmingapsize = mingap
207 self._srmingapsize = mingap
208
208
209 def start(self, rev):
209 def start(self, rev):
210 if rev == 0:
210 if rev == 0:
211 return 0
211 return 0
212 return self._data[rev - 1]
212 return self._data[rev - 1]
213
213
214 def end(self, rev):
214 def end(self, rev):
215 return self._data[rev]
215 return self._data[rev]
216
216
217 def length(self, rev):
217 def length(self, rev):
218 return self.end(rev) - self.start(rev)
218 return self.end(rev) - self.start(rev)
219
219
220 def __len__(self):
220 def __len__(self):
221 return len(self._data)
221 return len(self._data)
222
222
223 def _trimchunk(revlog, revs, startidx, endidx=None):
223 def _trimchunk(revlog, revs, startidx, endidx=None):
224 """returns revs[startidx:endidx] without empty trailing revs
224 """returns revs[startidx:endidx] without empty trailing revs
225
225
226 Doctest Setup
226 Doctest Setup
227 >>> revlog = _testrevlog([
227 >>> revlog = _testrevlog([
228 ... 5, #0
228 ... 5, #0
229 ... 10, #1
229 ... 10, #1
230 ... 12, #2
230 ... 12, #2
231 ... 12, #3 (empty)
231 ... 12, #3 (empty)
232 ... 17, #4
232 ... 17, #4
233 ... 21, #5
233 ... 21, #5
234 ... 21, #6 (empty)
234 ... 21, #6 (empty)
235 ... ])
235 ... ])
236
236
237 Contiguous cases:
237 Contiguous cases:
238 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0)
238 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0)
239 [0, 1, 2, 3, 4, 5]
239 [0, 1, 2, 3, 4, 5]
240 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 5)
240 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 5)
241 [0, 1, 2, 3, 4]
241 [0, 1, 2, 3, 4]
242 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 4)
242 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 4)
243 [0, 1, 2]
243 [0, 1, 2]
244 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 2, 4)
244 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 2, 4)
245 [2]
245 [2]
246 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3)
246 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3)
247 [3, 4, 5]
247 [3, 4, 5]
248 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3, 5)
248 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3, 5)
249 [3, 4]
249 [3, 4]
250
250
251 Discontiguous cases:
251 Discontiguous cases:
252 >>> _trimchunk(revlog, [1, 3, 5, 6], 0)
252 >>> _trimchunk(revlog, [1, 3, 5, 6], 0)
253 [1, 3, 5]
253 [1, 3, 5]
254 >>> _trimchunk(revlog, [1, 3, 5, 6], 0, 2)
254 >>> _trimchunk(revlog, [1, 3, 5, 6], 0, 2)
255 [1]
255 [1]
256 >>> _trimchunk(revlog, [1, 3, 5, 6], 1, 3)
256 >>> _trimchunk(revlog, [1, 3, 5, 6], 1, 3)
257 [3, 5]
257 [3, 5]
258 >>> _trimchunk(revlog, [1, 3, 5, 6], 1)
258 >>> _trimchunk(revlog, [1, 3, 5, 6], 1)
259 [3, 5]
259 [3, 5]
260 """
260 """
261 length = revlog.length
261 length = revlog.length
262
262
263 if endidx is None:
263 if endidx is None:
264 endidx = len(revs)
264 endidx = len(revs)
265
265
266 # Trim empty revs at the end, but never the very first revision of a chain
266 # Trim empty revs at the end, but never the very first revision of a chain
267 while endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0:
267 while endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0:
268 endidx -= 1
268 endidx -= 1
269
269
270 return revs[startidx:endidx]
270 return revs[startidx:endidx]
271
271
272 def _segmentspan(revlog, revs):
272 def _segmentspan(revlog, revs):
273 """Get the byte span of a segment of revisions
273 """Get the byte span of a segment of revisions
274
274
275 revs is a sorted array of revision numbers
275 revs is a sorted array of revision numbers
276
276
277 >>> revlog = _testrevlog([
277 >>> revlog = _testrevlog([
278 ... 5, #0
278 ... 5, #0
279 ... 10, #1
279 ... 10, #1
280 ... 12, #2
280 ... 12, #2
281 ... 12, #3 (empty)
281 ... 12, #3 (empty)
282 ... 17, #4
282 ... 17, #4
283 ... ])
283 ... ])
284
284
285 >>> _segmentspan(revlog, [0, 1, 2, 3, 4])
285 >>> _segmentspan(revlog, [0, 1, 2, 3, 4])
286 17
286 17
287 >>> _segmentspan(revlog, [0, 4])
287 >>> _segmentspan(revlog, [0, 4])
288 17
288 17
289 >>> _segmentspan(revlog, [3, 4])
289 >>> _segmentspan(revlog, [3, 4])
290 5
290 5
291 >>> _segmentspan(revlog, [1, 2, 3,])
291 >>> _segmentspan(revlog, [1, 2, 3,])
292 7
292 7
293 >>> _segmentspan(revlog, [1, 3])
293 >>> _segmentspan(revlog, [1, 3])
294 7
294 7
295 """
295 """
296 if not revs:
296 if not revs:
297 return 0
297 return 0
298 return revlog.end(revs[-1]) - revlog.start(revs[0])
298 return revlog.end(revs[-1]) - revlog.start(revs[0])
299
299
300 def _slicechunk(revlog, revs, deltainfo=None, targetsize=None):
300 def _slicechunk(revlog, revs, deltainfo=None, targetsize=None):
301 """slice revs to reduce the amount of unrelated data to be read from disk.
301 """slice revs to reduce the amount of unrelated data to be read from disk.
302
302
303 ``revs`` is sliced into groups that should be read in one time.
303 ``revs`` is sliced into groups that should be read in one time.
304 Assume that revs are sorted.
304 Assume that revs are sorted.
305
305
306 The initial chunk is sliced until the overall density (payload/chunks-span
306 The initial chunk is sliced until the overall density (payload/chunks-span
307 ratio) is above `revlog._srdensitythreshold`. No gap smaller than
307 ratio) is above `revlog._srdensitythreshold`. No gap smaller than
308 `revlog._srmingapsize` is skipped.
308 `revlog._srmingapsize` is skipped.
309
309
310 If `targetsize` is set, no chunk larger than `targetsize` will be yield.
310 If `targetsize` is set, no chunk larger than `targetsize` will be yield.
311 For consistency with other slicing choice, this limit won't go lower than
311 For consistency with other slicing choice, this limit won't go lower than
312 `revlog._srmingapsize`.
312 `revlog._srmingapsize`.
313
313
314 If individual revisions chunk are larger than this limit, they will still
314 If individual revisions chunk are larger than this limit, they will still
315 be raised individually.
315 be raised individually.
316
316
317 >>> revlog = _testrevlog([
317 >>> revlog = _testrevlog([
318 ... 5, #00 (5)
318 ... 5, #00 (5)
319 ... 10, #01 (5)
319 ... 10, #01 (5)
320 ... 12, #02 (2)
320 ... 12, #02 (2)
321 ... 12, #03 (empty)
321 ... 12, #03 (empty)
322 ... 27, #04 (15)
322 ... 27, #04 (15)
323 ... 31, #05 (4)
323 ... 31, #05 (4)
324 ... 31, #06 (empty)
324 ... 31, #06 (empty)
325 ... 42, #07 (11)
325 ... 42, #07 (11)
326 ... 47, #08 (5)
326 ... 47, #08 (5)
327 ... 47, #09 (empty)
327 ... 47, #09 (empty)
328 ... 48, #10 (1)
328 ... 48, #10 (1)
329 ... 51, #11 (3)
329 ... 51, #11 (3)
330 ... 74, #12 (23)
330 ... 74, #12 (23)
331 ... 85, #13 (11)
331 ... 85, #13 (11)
332 ... 86, #14 (1)
332 ... 86, #14 (1)
333 ... 91, #15 (5)
333 ... 91, #15 (5)
334 ... ])
334 ... ])
335
335
336 >>> list(_slicechunk(revlog, list(range(16))))
336 >>> list(_slicechunk(revlog, list(range(16))))
337 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
337 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
338 >>> list(_slicechunk(revlog, [0, 15]))
338 >>> list(_slicechunk(revlog, [0, 15]))
339 [[0], [15]]
339 [[0], [15]]
340 >>> list(_slicechunk(revlog, [0, 11, 15]))
340 >>> list(_slicechunk(revlog, [0, 11, 15]))
341 [[0], [11], [15]]
341 [[0], [11], [15]]
342 >>> list(_slicechunk(revlog, [0, 11, 13, 15]))
342 >>> list(_slicechunk(revlog, [0, 11, 13, 15]))
343 [[0], [11, 13, 15]]
343 [[0], [11, 13, 15]]
344 >>> list(_slicechunk(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
344 >>> list(_slicechunk(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
345 [[1, 2], [5, 8, 10, 11], [14]]
345 [[1, 2], [5, 8, 10, 11], [14]]
346
346
347 Slicing with a maximum chunk size
347 Slicing with a maximum chunk size
348 >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=15))
348 >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=15))
349 [[0], [11], [13], [15]]
349 [[0], [11], [13], [15]]
350 >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=20))
350 >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=20))
351 [[0], [11], [13, 15]]
351 [[0], [11], [13, 15]]
352 """
352 """
353 if targetsize is not None:
353 if targetsize is not None:
354 targetsize = max(targetsize, revlog._srmingapsize)
354 targetsize = max(targetsize, revlog._srmingapsize)
355 # targetsize should not be specified when evaluating delta candidates:
355 # targetsize should not be specified when evaluating delta candidates:
356 # * targetsize is used to ensure we stay within specification when reading,
356 # * targetsize is used to ensure we stay within specification when reading,
357 # * deltainfo is used to pick are good delta chain when writing.
357 # * deltainfo is used to pick are good delta chain when writing.
358 if not (deltainfo is None or targetsize is None):
358 if not (deltainfo is None or targetsize is None):
359 msg = 'cannot use `targetsize` with a `deltainfo`'
359 msg = 'cannot use `targetsize` with a `deltainfo`'
360 raise error.ProgrammingError(msg)
360 raise error.ProgrammingError(msg)
361 for chunk in _slicechunktodensity(revlog, revs,
361 for chunk in _slicechunktodensity(revlog, revs,
362 deltainfo,
362 deltainfo,
363 revlog._srdensitythreshold,
363 revlog._srdensitythreshold,
364 revlog._srmingapsize):
364 revlog._srmingapsize):
365 for subchunk in _slicechunktosize(revlog, chunk, targetsize):
365 for subchunk in _slicechunktosize(revlog, chunk, targetsize):
366 yield subchunk
366 yield subchunk
367
367
368 def _slicechunktosize(revlog, revs, targetsize=None):
368 def _slicechunktosize(revlog, revs, targetsize=None):
369 """slice revs to match the target size
369 """slice revs to match the target size
370
370
371 This is intended to be used on chunk that density slicing selected by that
371 This is intended to be used on chunk that density slicing selected by that
372 are still too large compared to the read garantee of revlog. This might
372 are still too large compared to the read garantee of revlog. This might
373 happens when "minimal gap size" interrupted the slicing or when chain are
373 happens when "minimal gap size" interrupted the slicing or when chain are
374 built in a way that create large blocks next to each other.
374 built in a way that create large blocks next to each other.
375
375
376 >>> revlog = _testrevlog([
376 >>> revlog = _testrevlog([
377 ... 3, #0 (3)
377 ... 3, #0 (3)
378 ... 5, #1 (2)
378 ... 5, #1 (2)
379 ... 6, #2 (1)
379 ... 6, #2 (1)
380 ... 8, #3 (2)
380 ... 8, #3 (2)
381 ... 8, #4 (empty)
381 ... 8, #4 (empty)
382 ... 11, #5 (3)
382 ... 11, #5 (3)
383 ... 12, #6 (1)
383 ... 12, #6 (1)
384 ... 13, #7 (1)
384 ... 13, #7 (1)
385 ... 14, #8 (1)
385 ... 14, #8 (1)
386 ... ])
386 ... ])
387
387
388 Cases where chunk is already small enough
388 Cases where chunk is already small enough
389 >>> list(_slicechunktosize(revlog, [0], 3))
389 >>> list(_slicechunktosize(revlog, [0], 3))
390 [[0]]
390 [[0]]
391 >>> list(_slicechunktosize(revlog, [6, 7], 3))
391 >>> list(_slicechunktosize(revlog, [6, 7], 3))
392 [[6, 7]]
392 [[6, 7]]
393 >>> list(_slicechunktosize(revlog, [0], None))
393 >>> list(_slicechunktosize(revlog, [0], None))
394 [[0]]
394 [[0]]
395 >>> list(_slicechunktosize(revlog, [6, 7], None))
395 >>> list(_slicechunktosize(revlog, [6, 7], None))
396 [[6, 7]]
396 [[6, 7]]
397
397
398 cases where we need actual slicing
398 cases where we need actual slicing
399 >>> list(_slicechunktosize(revlog, [0, 1], 3))
399 >>> list(_slicechunktosize(revlog, [0, 1], 3))
400 [[0], [1]]
400 [[0], [1]]
401 >>> list(_slicechunktosize(revlog, [1, 3], 3))
401 >>> list(_slicechunktosize(revlog, [1, 3], 3))
402 [[1], [3]]
402 [[1], [3]]
403 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
403 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
404 [[1, 2], [3]]
404 [[1, 2], [3]]
405 >>> list(_slicechunktosize(revlog, [3, 5], 3))
405 >>> list(_slicechunktosize(revlog, [3, 5], 3))
406 [[3], [5]]
406 [[3], [5]]
407 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
407 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
408 [[3], [5]]
408 [[3], [5]]
409 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
409 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
410 [[5], [6, 7, 8]]
410 [[5], [6, 7, 8]]
411 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
411 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
412 [[0], [1, 2], [3], [5], [6, 7, 8]]
412 [[0], [1, 2], [3], [5], [6, 7, 8]]
413
413
414 Case with too large individual chunk (must return valid chunk)
414 Case with too large individual chunk (must return valid chunk)
415 >>> list(_slicechunktosize(revlog, [0, 1], 2))
415 >>> list(_slicechunktosize(revlog, [0, 1], 2))
416 [[0], [1]]
416 [[0], [1]]
417 >>> list(_slicechunktosize(revlog, [1, 3], 1))
417 >>> list(_slicechunktosize(revlog, [1, 3], 1))
418 [[1], [3]]
418 [[1], [3]]
419 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
419 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
420 [[3], [5]]
420 [[3], [5]]
421 """
421 """
422 assert targetsize is None or 0 <= targetsize
422 assert targetsize is None or 0 <= targetsize
423 if targetsize is None or _segmentspan(revlog, revs) <= targetsize:
423 if targetsize is None or _segmentspan(revlog, revs) <= targetsize:
424 yield revs
424 yield revs
425 return
425 return
426
426
427 startrevidx = 0
427 startrevidx = 0
428 startdata = revlog.start(revs[0])
428 startdata = revlog.start(revs[0])
429 endrevidx = 0
429 endrevidx = 0
430 iterrevs = enumerate(revs)
430 iterrevs = enumerate(revs)
431 next(iterrevs) # skip first rev.
431 next(iterrevs) # skip first rev.
432 for idx, r in iterrevs:
432 for idx, r in iterrevs:
433 span = revlog.end(r) - startdata
433 span = revlog.end(r) - startdata
434 if span <= targetsize:
434 if span <= targetsize:
435 endrevidx = idx
435 endrevidx = idx
436 else:
436 else:
437 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx + 1)
437 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx + 1)
438 if chunk:
438 if chunk:
439 yield chunk
439 yield chunk
440 startrevidx = idx
440 startrevidx = idx
441 startdata = revlog.start(r)
441 startdata = revlog.start(r)
442 endrevidx = idx
442 endrevidx = idx
443 yield _trimchunk(revlog, revs, startrevidx)
443 yield _trimchunk(revlog, revs, startrevidx)
444
444
445 def _slicechunktodensity(revlog, revs, deltainfo=None, targetdensity=0.5,
445 def _slicechunktodensity(revlog, revs, deltainfo=None, targetdensity=0.5,
446 mingapsize=0):
446 mingapsize=0):
447 """slice revs to reduce the amount of unrelated data to be read from disk.
447 """slice revs to reduce the amount of unrelated data to be read from disk.
448
448
449 ``revs`` is sliced into groups that should be read in one time.
449 ``revs`` is sliced into groups that should be read in one time.
450 Assume that revs are sorted.
450 Assume that revs are sorted.
451
451
452 ``deltainfo`` is a _deltainfo instance of a revision that we would append
452 ``deltainfo`` is a _deltainfo instance of a revision that we would append
453 to the top of the revlog.
453 to the top of the revlog.
454
454
455 The initial chunk is sliced until the overall density (payload/chunks-span
455 The initial chunk is sliced until the overall density (payload/chunks-span
456 ratio) is above `targetdensity`. No gap smaller than `mingapsize` is
456 ratio) is above `targetdensity`. No gap smaller than `mingapsize` is
457 skipped.
457 skipped.
458
458
459 >>> revlog = _testrevlog([
459 >>> revlog = _testrevlog([
460 ... 5, #00 (5)
460 ... 5, #00 (5)
461 ... 10, #01 (5)
461 ... 10, #01 (5)
462 ... 12, #02 (2)
462 ... 12, #02 (2)
463 ... 12, #03 (empty)
463 ... 12, #03 (empty)
464 ... 27, #04 (15)
464 ... 27, #04 (15)
465 ... 31, #05 (4)
465 ... 31, #05 (4)
466 ... 31, #06 (empty)
466 ... 31, #06 (empty)
467 ... 42, #07 (11)
467 ... 42, #07 (11)
468 ... 47, #08 (5)
468 ... 47, #08 (5)
469 ... 47, #09 (empty)
469 ... 47, #09 (empty)
470 ... 48, #10 (1)
470 ... 48, #10 (1)
471 ... 51, #11 (3)
471 ... 51, #11 (3)
472 ... 74, #12 (23)
472 ... 74, #12 (23)
473 ... 85, #13 (11)
473 ... 85, #13 (11)
474 ... 86, #14 (1)
474 ... 86, #14 (1)
475 ... 91, #15 (5)
475 ... 91, #15 (5)
476 ... ])
476 ... ])
477
477
478 >>> list(_slicechunktodensity(revlog, list(range(16))))
478 >>> list(_slicechunktodensity(revlog, list(range(16))))
479 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
479 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
480 >>> list(_slicechunktodensity(revlog, [0, 15]))
480 >>> list(_slicechunktodensity(revlog, [0, 15]))
481 [[0], [15]]
481 [[0], [15]]
482 >>> list(_slicechunktodensity(revlog, [0, 11, 15]))
482 >>> list(_slicechunktodensity(revlog, [0, 11, 15]))
483 [[0], [11], [15]]
483 [[0], [11], [15]]
484 >>> list(_slicechunktodensity(revlog, [0, 11, 13, 15]))
484 >>> list(_slicechunktodensity(revlog, [0, 11, 13, 15]))
485 [[0], [11, 13, 15]]
485 [[0], [11, 13, 15]]
486 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
486 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
487 [[1, 2], [5, 8, 10, 11], [14]]
487 [[1, 2], [5, 8, 10, 11], [14]]
488 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
488 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
489 ... mingapsize=20))
489 ... mingapsize=20))
490 [[1, 2, 3, 5, 8, 10, 11], [14]]
490 [[1, 2, 3, 5, 8, 10, 11], [14]]
491 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
491 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
492 ... targetdensity=0.95))
492 ... targetdensity=0.95))
493 [[1, 2], [5], [8, 10, 11], [14]]
493 [[1, 2], [5], [8, 10, 11], [14]]
494 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
494 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
495 ... targetdensity=0.95, mingapsize=12))
495 ... targetdensity=0.95, mingapsize=12))
496 [[1, 2], [5, 8, 10, 11], [14]]
496 [[1, 2], [5, 8, 10, 11], [14]]
497 """
497 """
498 start = revlog.start
498 start = revlog.start
499 length = revlog.length
499 length = revlog.length
500
500
501 if len(revs) <= 1:
501 if len(revs) <= 1:
502 yield revs
502 yield revs
503 return
503 return
504
504
505 nextrev = len(revlog)
505 nextrev = len(revlog)
506 nextoffset = revlog.end(nextrev - 1)
506 nextoffset = revlog.end(nextrev - 1)
507
507
508 if deltainfo is None:
508 if deltainfo is None:
509 deltachainspan = _segmentspan(revlog, revs)
509 deltachainspan = _segmentspan(revlog, revs)
510 chainpayload = sum(length(r) for r in revs)
510 chainpayload = sum(length(r) for r in revs)
511 else:
511 else:
512 deltachainspan = deltainfo.distance
512 deltachainspan = deltainfo.distance
513 chainpayload = deltainfo.compresseddeltalen
513 chainpayload = deltainfo.compresseddeltalen
514
514
515 if deltachainspan < mingapsize:
515 if deltachainspan < mingapsize:
516 yield revs
516 yield revs
517 return
517 return
518
518
519 readdata = deltachainspan
519 readdata = deltachainspan
520
520
521 if deltachainspan:
521 if deltachainspan:
522 density = chainpayload / float(deltachainspan)
522 density = chainpayload / float(deltachainspan)
523 else:
523 else:
524 density = 1.0
524 density = 1.0
525
525
526 if density >= targetdensity:
526 if density >= targetdensity:
527 yield revs
527 yield revs
528 return
528 return
529
529
530 if deltainfo is not None:
530 if deltainfo is not None:
531 revs = list(revs)
531 revs = list(revs)
532 revs.append(nextrev)
532 revs.append(nextrev)
533
533
534 # Store the gaps in a heap to have them sorted by decreasing size
534 # Store the gaps in a heap to have them sorted by decreasing size
535 gapsheap = []
535 gapsheap = []
536 heapq.heapify(gapsheap)
536 heapq.heapify(gapsheap)
537 prevend = None
537 prevend = None
538 for i, rev in enumerate(revs):
538 for i, rev in enumerate(revs):
539 if rev < nextrev:
539 if rev < nextrev:
540 revstart = start(rev)
540 revstart = start(rev)
541 revlen = length(rev)
541 revlen = length(rev)
542 else:
542 else:
543 revstart = nextoffset
543 revstart = nextoffset
544 revlen = deltainfo.deltalen
544 revlen = deltainfo.deltalen
545
545
546 # Skip empty revisions to form larger holes
546 # Skip empty revisions to form larger holes
547 if revlen == 0:
547 if revlen == 0:
548 continue
548 continue
549
549
550 if prevend is not None:
550 if prevend is not None:
551 gapsize = revstart - prevend
551 gapsize = revstart - prevend
552 # only consider holes that are large enough
552 # only consider holes that are large enough
553 if gapsize > mingapsize:
553 if gapsize > mingapsize:
554 heapq.heappush(gapsheap, (-gapsize, i))
554 heapq.heappush(gapsheap, (-gapsize, i))
555
555
556 prevend = revstart + revlen
556 prevend = revstart + revlen
557
557
558 # Collect the indices of the largest holes until the density is acceptable
558 # Collect the indices of the largest holes until the density is acceptable
559 indicesheap = []
559 indicesheap = []
560 heapq.heapify(indicesheap)
560 heapq.heapify(indicesheap)
561 while gapsheap and density < targetdensity:
561 while gapsheap and density < targetdensity:
562 oppgapsize, gapidx = heapq.heappop(gapsheap)
562 oppgapsize, gapidx = heapq.heappop(gapsheap)
563
563
564 heapq.heappush(indicesheap, gapidx)
564 heapq.heappush(indicesheap, gapidx)
565
565
566 # the gap sizes are stored as negatives to be sorted decreasingly
566 # the gap sizes are stored as negatives to be sorted decreasingly
567 # by the heap
567 # by the heap
568 readdata -= (-oppgapsize)
568 readdata -= (-oppgapsize)
569 if readdata > 0:
569 if readdata > 0:
570 density = chainpayload / float(readdata)
570 density = chainpayload / float(readdata)
571 else:
571 else:
572 density = 1.0
572 density = 1.0
573
573
574 # Cut the revs at collected indices
574 # Cut the revs at collected indices
575 previdx = 0
575 previdx = 0
576 while indicesheap:
576 while indicesheap:
577 idx = heapq.heappop(indicesheap)
577 idx = heapq.heappop(indicesheap)
578
578
579 chunk = _trimchunk(revlog, revs, previdx, idx)
579 chunk = _trimchunk(revlog, revs, previdx, idx)
580 if chunk:
580 if chunk:
581 yield chunk
581 yield chunk
582
582
583 previdx = idx
583 previdx = idx
584
584
585 chunk = _trimchunk(revlog, revs, previdx)
585 chunk = _trimchunk(revlog, revs, previdx)
586 if chunk:
586 if chunk:
587 yield chunk
587 yield chunk
588
588
589 @attr.s(slots=True, frozen=True)
589 @attr.s(slots=True, frozen=True)
590 class _deltainfo(object):
590 class _deltainfo(object):
591 distance = attr.ib()
591 distance = attr.ib()
592 deltalen = attr.ib()
592 deltalen = attr.ib()
593 data = attr.ib()
593 data = attr.ib()
594 base = attr.ib()
594 base = attr.ib()
595 chainbase = attr.ib()
595 chainbase = attr.ib()
596 chainlen = attr.ib()
596 chainlen = attr.ib()
597 compresseddeltalen = attr.ib()
597 compresseddeltalen = attr.ib()
598
598
599 class _deltacomputer(object):
599 class _deltacomputer(object):
600 def __init__(self, revlog):
600 def __init__(self, revlog):
601 self.revlog = revlog
601 self.revlog = revlog
602
602
603 def _getcandidaterevs(self, p1, p2, cachedelta):
603 def _getcandidaterevs(self, p1, p2, cachedelta):
604 """
604 """
605 Provides revisions that present an interest to be diffed against,
605 Provides revisions that present an interest to be diffed against,
606 grouped by level of easiness.
606 grouped by level of easiness.
607 """
607 """
608 revlog = self.revlog
608 revlog = self.revlog
609 gdelta = revlog._generaldelta
609 gdelta = revlog._generaldelta
610 curr = len(revlog)
610 curr = len(revlog)
611 prev = curr - 1
611 prev = curr - 1
612 p1r, p2r = revlog.rev(p1), revlog.rev(p2)
612 p1r, p2r = revlog.rev(p1), revlog.rev(p2)
613
613
614 # should we try to build a delta?
614 # should we try to build a delta?
615 if prev != nullrev and revlog.storedeltachains:
615 if prev != nullrev and revlog.storedeltachains:
616 tested = set()
616 tested = set()
617 # This condition is true most of the time when processing
617 # This condition is true most of the time when processing
618 # changegroup data into a generaldelta repo. The only time it
618 # changegroup data into a generaldelta repo. The only time it
619 # isn't true is if this is the first revision in a delta chain
619 # isn't true is if this is the first revision in a delta chain
620 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
620 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
621 if cachedelta and gdelta and revlog._lazydeltabase:
621 if cachedelta and gdelta and revlog._lazydeltabase:
622 # Assume what we received from the server is a good choice
622 # Assume what we received from the server is a good choice
623 # build delta will reuse the cache
623 # build delta will reuse the cache
624 yield (cachedelta[0],)
624 yield (cachedelta[0],)
625 tested.add(cachedelta[0])
625 tested.add(cachedelta[0])
626
626
627 if gdelta:
627 if gdelta:
628 # exclude already lazy tested base if any
628 # exclude already lazy tested base if any
629 parents = [p for p in (p1r, p2r)
629 parents = [p for p in (p1r, p2r)
630 if p != nullrev and p not in tested]
630 if p != nullrev and p not in tested]
631
631
632 if not revlog._deltabothparents and len(parents) == 2:
632 if not revlog._deltabothparents and len(parents) == 2:
633 parents.sort()
633 parents.sort()
634 # To minimize the chance of having to build a fulltext,
634 # To minimize the chance of having to build a fulltext,
635 # pick first whichever parent is closest to us (max rev)
635 # pick first whichever parent is closest to us (max rev)
636 yield (parents[1],)
636 yield (parents[1],)
637 # then the other one (min rev) if the first did not fit
637 # then the other one (min rev) if the first did not fit
638 yield (parents[0],)
638 yield (parents[0],)
639 tested.update(parents)
639 tested.update(parents)
640 elif len(parents) > 0:
640 elif len(parents) > 0:
641 # Test all parents (1 or 2), and keep the best candidate
641 # Test all parents (1 or 2), and keep the best candidate
642 yield parents
642 yield parents
643 tested.update(parents)
643 tested.update(parents)
644
644
645 if prev not in tested:
645 if prev not in tested:
646 # other approach failed try against prev to hopefully save us a
646 # other approach failed try against prev to hopefully save us a
647 # fulltext.
647 # fulltext.
648 yield (prev,)
648 yield (prev,)
649 tested.add(prev)
649 tested.add(prev)
650
650
651 def buildtext(self, revinfo, fh):
651 def buildtext(self, revinfo, fh):
652 """Builds a fulltext version of a revision
652 """Builds a fulltext version of a revision
653
653
654 revinfo: _revisioninfo instance that contains all needed info
654 revinfo: _revisioninfo instance that contains all needed info
655 fh: file handle to either the .i or the .d revlog file,
655 fh: file handle to either the .i or the .d revlog file,
656 depending on whether it is inlined or not
656 depending on whether it is inlined or not
657 """
657 """
658 btext = revinfo.btext
658 btext = revinfo.btext
659 if btext[0] is not None:
659 if btext[0] is not None:
660 return btext[0]
660 return btext[0]
661
661
662 revlog = self.revlog
662 revlog = self.revlog
663 cachedelta = revinfo.cachedelta
663 cachedelta = revinfo.cachedelta
664 flags = revinfo.flags
664 flags = revinfo.flags
665 node = revinfo.node
665 node = revinfo.node
666
666
667 baserev = cachedelta[0]
667 baserev = cachedelta[0]
668 delta = cachedelta[1]
668 delta = cachedelta[1]
669 # special case deltas which replace entire base; no need to decode
669 # special case deltas which replace entire base; no need to decode
670 # base revision. this neatly avoids censored bases, which throw when
670 # base revision. this neatly avoids censored bases, which throw when
671 # they're decoded.
671 # they're decoded.
672 hlen = struct.calcsize(">lll")
672 hlen = struct.calcsize(">lll")
673 if delta[:hlen] == mdiff.replacediffheader(revlog.rawsize(baserev),
673 if delta[:hlen] == mdiff.replacediffheader(revlog.rawsize(baserev),
674 len(delta) - hlen):
674 len(delta) - hlen):
675 btext[0] = delta[hlen:]
675 btext[0] = delta[hlen:]
676 else:
676 else:
677 # deltabase is rawtext before changed by flag processors, which is
677 # deltabase is rawtext before changed by flag processors, which is
678 # equivalent to non-raw text
678 # equivalent to non-raw text
679 basetext = revlog.revision(baserev, _df=fh, raw=False)
679 basetext = revlog.revision(baserev, _df=fh, raw=False)
680 btext[0] = mdiff.patch(basetext, delta)
680 btext[0] = mdiff.patch(basetext, delta)
681
681
682 try:
682 try:
683 res = revlog._processflags(btext[0], flags, 'read', raw=True)
683 res = revlog._processflags(btext[0], flags, 'read', raw=True)
684 btext[0], validatehash = res
684 btext[0], validatehash = res
685 if validatehash:
685 if validatehash:
686 revlog.checkhash(btext[0], node, p1=revinfo.p1, p2=revinfo.p2)
686 revlog.checkhash(btext[0], node, p1=revinfo.p1, p2=revinfo.p2)
687 if flags & REVIDX_ISCENSORED:
687 if flags & REVIDX_ISCENSORED:
688 raise RevlogError(_('node %s is not censored') % node)
688 raise RevlogError(_('node %s is not censored') % node)
689 except CensoredNodeError:
689 except CensoredNodeError:
690 # must pass the censored index flag to add censored revisions
690 # must pass the censored index flag to add censored revisions
691 if not flags & REVIDX_ISCENSORED:
691 if not flags & REVIDX_ISCENSORED:
692 raise
692 raise
693 return btext[0]
693 return btext[0]
694
694
695 def _builddeltadiff(self, base, revinfo, fh):
695 def _builddeltadiff(self, base, revinfo, fh):
696 revlog = self.revlog
696 revlog = self.revlog
697 t = self.buildtext(revinfo, fh)
697 t = self.buildtext(revinfo, fh)
698 if revlog.iscensored(base):
698 if revlog.iscensored(base):
699 # deltas based on a censored revision must replace the
699 # deltas based on a censored revision must replace the
700 # full content in one patch, so delta works everywhere
700 # full content in one patch, so delta works everywhere
701 header = mdiff.replacediffheader(revlog.rawsize(base), len(t))
701 header = mdiff.replacediffheader(revlog.rawsize(base), len(t))
702 delta = header + t
702 delta = header + t
703 else:
703 else:
704 ptext = revlog.revision(base, _df=fh, raw=True)
704 ptext = revlog.revision(base, _df=fh, raw=True)
705 delta = mdiff.textdiff(ptext, t)
705 delta = mdiff.textdiff(ptext, t)
706
706
707 return delta
707 return delta
708
708
709 def _builddeltainfo(self, revinfo, base, fh):
709 def _builddeltainfo(self, revinfo, base, fh):
710 # can we use the cached delta?
710 # can we use the cached delta?
711 if revinfo.cachedelta and revinfo.cachedelta[0] == base:
711 if revinfo.cachedelta and revinfo.cachedelta[0] == base:
712 delta = revinfo.cachedelta[1]
712 delta = revinfo.cachedelta[1]
713 else:
713 else:
714 delta = self._builddeltadiff(base, revinfo, fh)
714 delta = self._builddeltadiff(base, revinfo, fh)
715 revlog = self.revlog
715 revlog = self.revlog
716 header, data = revlog.compress(delta)
716 header, data = revlog.compress(delta)
717 deltalen = len(header) + len(data)
717 deltalen = len(header) + len(data)
718 chainbase = revlog.chainbase(base)
718 chainbase = revlog.chainbase(base)
719 offset = revlog.end(len(revlog) - 1)
719 offset = revlog.end(len(revlog) - 1)
720 dist = deltalen + offset - revlog.start(chainbase)
720 dist = deltalen + offset - revlog.start(chainbase)
721 if revlog._generaldelta:
721 if revlog._generaldelta:
722 deltabase = base
722 deltabase = base
723 else:
723 else:
724 deltabase = chainbase
724 deltabase = chainbase
725 chainlen, compresseddeltalen = revlog._chaininfo(base)
725 chainlen, compresseddeltalen = revlog._chaininfo(base)
726 chainlen += 1
726 chainlen += 1
727 compresseddeltalen += deltalen
727 compresseddeltalen += deltalen
728 return _deltainfo(dist, deltalen, (header, data), deltabase,
728 return _deltainfo(dist, deltalen, (header, data), deltabase,
729 chainbase, chainlen, compresseddeltalen)
729 chainbase, chainlen, compresseddeltalen)
730
730
731 def finddeltainfo(self, revinfo, fh):
731 def finddeltainfo(self, revinfo, fh):
732 """Find an acceptable delta against a candidate revision
732 """Find an acceptable delta against a candidate revision
733
733
734 revinfo: information about the revision (instance of _revisioninfo)
734 revinfo: information about the revision (instance of _revisioninfo)
735 fh: file handle to either the .i or the .d revlog file,
735 fh: file handle to either the .i or the .d revlog file,
736 depending on whether it is inlined or not
736 depending on whether it is inlined or not
737
737
738 Returns the first acceptable candidate revision, as ordered by
738 Returns the first acceptable candidate revision, as ordered by
739 _getcandidaterevs
739 _getcandidaterevs
740 """
740 """
741 cachedelta = revinfo.cachedelta
741 cachedelta = revinfo.cachedelta
742 p1 = revinfo.p1
742 p1 = revinfo.p1
743 p2 = revinfo.p2
743 p2 = revinfo.p2
744 revlog = self.revlog
744 revlog = self.revlog
745
745
746 deltalength = self.revlog.length
747 deltaparent = self.revlog.deltaparent
748
746 deltainfo = None
749 deltainfo = None
747 for candidaterevs in self._getcandidaterevs(p1, p2, cachedelta):
750 for candidaterevs in self._getcandidaterevs(p1, p2, cachedelta):
748 nominateddeltas = []
751 nominateddeltas = []
749 for candidaterev in candidaterevs:
752 for candidaterev in candidaterevs:
753 # skip over empty delta (no need to include them in a chain)
754 while candidaterev != nullrev and not deltalength(candidaterev):
755 candidaterev = deltaparent(candidaterev)
756 # no need to try a delta against nullid, this will be handled
757 # by fulltext later.
758 if candidaterev == nullrev:
759 continue
750 # no delta for rawtext-changing revs (see "candelta" for why)
760 # no delta for rawtext-changing revs (see "candelta" for why)
751 if revlog.flags(candidaterev) & REVIDX_RAWTEXT_CHANGING_FLAGS:
761 if revlog.flags(candidaterev) & REVIDX_RAWTEXT_CHANGING_FLAGS:
752 continue
762 continue
753 candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh)
763 candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh)
754 if revlog._isgooddeltainfo(candidatedelta, revinfo):
764 if revlog._isgooddeltainfo(candidatedelta, revinfo):
755 nominateddeltas.append(candidatedelta)
765 nominateddeltas.append(candidatedelta)
756 if nominateddeltas:
766 if nominateddeltas:
757 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
767 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
758 break
768 break
759
769
760 return deltainfo
770 return deltainfo
761
771
762 @attr.s(slots=True, frozen=True)
772 @attr.s(slots=True, frozen=True)
763 class _revisioninfo(object):
773 class _revisioninfo(object):
764 """Information about a revision that allows building its fulltext
774 """Information about a revision that allows building its fulltext
765 node: expected hash of the revision
775 node: expected hash of the revision
766 p1, p2: parent revs of the revision
776 p1, p2: parent revs of the revision
767 btext: built text cache consisting of a one-element list
777 btext: built text cache consisting of a one-element list
768 cachedelta: (baserev, uncompressed_delta) or None
778 cachedelta: (baserev, uncompressed_delta) or None
769 flags: flags associated to the revision storage
779 flags: flags associated to the revision storage
770
780
771 One of btext[0] or cachedelta must be set.
781 One of btext[0] or cachedelta must be set.
772 """
782 """
773 node = attr.ib()
783 node = attr.ib()
774 p1 = attr.ib()
784 p1 = attr.ib()
775 p2 = attr.ib()
785 p2 = attr.ib()
776 btext = attr.ib()
786 btext = attr.ib()
777 textlen = attr.ib()
787 textlen = attr.ib()
778 cachedelta = attr.ib()
788 cachedelta = attr.ib()
779 flags = attr.ib()
789 flags = attr.ib()
780
790
781 # index v0:
791 # index v0:
782 # 4 bytes: offset
792 # 4 bytes: offset
783 # 4 bytes: compressed length
793 # 4 bytes: compressed length
784 # 4 bytes: base rev
794 # 4 bytes: base rev
785 # 4 bytes: link rev
795 # 4 bytes: link rev
786 # 20 bytes: parent 1 nodeid
796 # 20 bytes: parent 1 nodeid
787 # 20 bytes: parent 2 nodeid
797 # 20 bytes: parent 2 nodeid
788 # 20 bytes: nodeid
798 # 20 bytes: nodeid
789 indexformatv0 = struct.Struct(">4l20s20s20s")
799 indexformatv0 = struct.Struct(">4l20s20s20s")
790 indexformatv0_pack = indexformatv0.pack
800 indexformatv0_pack = indexformatv0.pack
791 indexformatv0_unpack = indexformatv0.unpack
801 indexformatv0_unpack = indexformatv0.unpack
792
802
793 class revlogoldindex(list):
803 class revlogoldindex(list):
794 def __getitem__(self, i):
804 def __getitem__(self, i):
795 if i == -1:
805 if i == -1:
796 return (0, 0, 0, -1, -1, -1, -1, nullid)
806 return (0, 0, 0, -1, -1, -1, -1, nullid)
797 return list.__getitem__(self, i)
807 return list.__getitem__(self, i)
798
808
799 class revlogoldio(object):
809 class revlogoldio(object):
800 def __init__(self):
810 def __init__(self):
801 self.size = indexformatv0.size
811 self.size = indexformatv0.size
802
812
803 def parseindex(self, data, inline):
813 def parseindex(self, data, inline):
804 s = self.size
814 s = self.size
805 index = []
815 index = []
806 nodemap = {nullid: nullrev}
816 nodemap = {nullid: nullrev}
807 n = off = 0
817 n = off = 0
808 l = len(data)
818 l = len(data)
809 while off + s <= l:
819 while off + s <= l:
810 cur = data[off:off + s]
820 cur = data[off:off + s]
811 off += s
821 off += s
812 e = indexformatv0_unpack(cur)
822 e = indexformatv0_unpack(cur)
813 # transform to revlogv1 format
823 # transform to revlogv1 format
814 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
824 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
815 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
825 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
816 index.append(e2)
826 index.append(e2)
817 nodemap[e[6]] = n
827 nodemap[e[6]] = n
818 n += 1
828 n += 1
819
829
820 return revlogoldindex(index), nodemap, None
830 return revlogoldindex(index), nodemap, None
821
831
822 def packentry(self, entry, node, version, rev):
832 def packentry(self, entry, node, version, rev):
823 if gettype(entry[0]):
833 if gettype(entry[0]):
824 raise RevlogError(_('index entry flags need revlog version 1'))
834 raise RevlogError(_('index entry flags need revlog version 1'))
825 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
835 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
826 node(entry[5]), node(entry[6]), entry[7])
836 node(entry[5]), node(entry[6]), entry[7])
827 return indexformatv0_pack(*e2)
837 return indexformatv0_pack(*e2)
828
838
829 # index ng:
839 # index ng:
830 # 6 bytes: offset
840 # 6 bytes: offset
831 # 2 bytes: flags
841 # 2 bytes: flags
832 # 4 bytes: compressed length
842 # 4 bytes: compressed length
833 # 4 bytes: uncompressed length
843 # 4 bytes: uncompressed length
834 # 4 bytes: base rev
844 # 4 bytes: base rev
835 # 4 bytes: link rev
845 # 4 bytes: link rev
836 # 4 bytes: parent 1 rev
846 # 4 bytes: parent 1 rev
837 # 4 bytes: parent 2 rev
847 # 4 bytes: parent 2 rev
838 # 32 bytes: nodeid
848 # 32 bytes: nodeid
839 indexformatng = struct.Struct(">Qiiiiii20s12x")
849 indexformatng = struct.Struct(">Qiiiiii20s12x")
840 indexformatng_pack = indexformatng.pack
850 indexformatng_pack = indexformatng.pack
841 versionformat = struct.Struct(">I")
851 versionformat = struct.Struct(">I")
842 versionformat_pack = versionformat.pack
852 versionformat_pack = versionformat.pack
843 versionformat_unpack = versionformat.unpack
853 versionformat_unpack = versionformat.unpack
844
854
845 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
855 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
846 # signed integer)
856 # signed integer)
847 _maxentrysize = 0x7fffffff
857 _maxentrysize = 0x7fffffff
848
858
849 class revlogio(object):
859 class revlogio(object):
850 def __init__(self):
860 def __init__(self):
851 self.size = indexformatng.size
861 self.size = indexformatng.size
852
862
853 def parseindex(self, data, inline):
863 def parseindex(self, data, inline):
854 # call the C implementation to parse the index data
864 # call the C implementation to parse the index data
855 index, cache = parsers.parse_index2(data, inline)
865 index, cache = parsers.parse_index2(data, inline)
856 return index, getattr(index, 'nodemap', None), cache
866 return index, getattr(index, 'nodemap', None), cache
857
867
858 def packentry(self, entry, node, version, rev):
868 def packentry(self, entry, node, version, rev):
859 p = indexformatng_pack(*entry)
869 p = indexformatng_pack(*entry)
860 if rev == 0:
870 if rev == 0:
861 p = versionformat_pack(version) + p[4:]
871 p = versionformat_pack(version) + p[4:]
862 return p
872 return p
863
873
864 class revlog(object):
874 class revlog(object):
865 """
875 """
866 the underlying revision storage object
876 the underlying revision storage object
867
877
868 A revlog consists of two parts, an index and the revision data.
878 A revlog consists of two parts, an index and the revision data.
869
879
870 The index is a file with a fixed record size containing
880 The index is a file with a fixed record size containing
871 information on each revision, including its nodeid (hash), the
881 information on each revision, including its nodeid (hash), the
872 nodeids of its parents, the position and offset of its data within
882 nodeids of its parents, the position and offset of its data within
873 the data file, and the revision it's based on. Finally, each entry
883 the data file, and the revision it's based on. Finally, each entry
874 contains a linkrev entry that can serve as a pointer to external
884 contains a linkrev entry that can serve as a pointer to external
875 data.
885 data.
876
886
877 The revision data itself is a linear collection of data chunks.
887 The revision data itself is a linear collection of data chunks.
878 Each chunk represents a revision and is usually represented as a
888 Each chunk represents a revision and is usually represented as a
879 delta against the previous chunk. To bound lookup time, runs of
889 delta against the previous chunk. To bound lookup time, runs of
880 deltas are limited to about 2 times the length of the original
890 deltas are limited to about 2 times the length of the original
881 version data. This makes retrieval of a version proportional to
891 version data. This makes retrieval of a version proportional to
882 its size, or O(1) relative to the number of revisions.
892 its size, or O(1) relative to the number of revisions.
883
893
884 Both pieces of the revlog are written to in an append-only
894 Both pieces of the revlog are written to in an append-only
885 fashion, which means we never need to rewrite a file to insert or
895 fashion, which means we never need to rewrite a file to insert or
886 remove data, and can use some simple techniques to avoid the need
896 remove data, and can use some simple techniques to avoid the need
887 for locking while reading.
897 for locking while reading.
888
898
889 If checkambig, indexfile is opened with checkambig=True at
899 If checkambig, indexfile is opened with checkambig=True at
890 writing, to avoid file stat ambiguity.
900 writing, to avoid file stat ambiguity.
891
901
892 If mmaplargeindex is True, and an mmapindexthreshold is set, the
902 If mmaplargeindex is True, and an mmapindexthreshold is set, the
893 index will be mmapped rather than read if it is larger than the
903 index will be mmapped rather than read if it is larger than the
894 configured threshold.
904 configured threshold.
895
905
896 If censorable is True, the revlog can have censored revisions.
906 If censorable is True, the revlog can have censored revisions.
897 """
907 """
898 def __init__(self, opener, indexfile, datafile=None, checkambig=False,
908 def __init__(self, opener, indexfile, datafile=None, checkambig=False,
899 mmaplargeindex=False, censorable=False):
909 mmaplargeindex=False, censorable=False):
900 """
910 """
901 create a revlog object
911 create a revlog object
902
912
903 opener is a function that abstracts the file opening operation
913 opener is a function that abstracts the file opening operation
904 and can be used to implement COW semantics or the like.
914 and can be used to implement COW semantics or the like.
905 """
915 """
906 self.indexfile = indexfile
916 self.indexfile = indexfile
907 self.datafile = datafile or (indexfile[:-2] + ".d")
917 self.datafile = datafile or (indexfile[:-2] + ".d")
908 self.opener = opener
918 self.opener = opener
909 # When True, indexfile is opened with checkambig=True at writing, to
919 # When True, indexfile is opened with checkambig=True at writing, to
910 # avoid file stat ambiguity.
920 # avoid file stat ambiguity.
911 self._checkambig = checkambig
921 self._checkambig = checkambig
912 self._censorable = censorable
922 self._censorable = censorable
913 # 3-tuple of (node, rev, text) for a raw revision.
923 # 3-tuple of (node, rev, text) for a raw revision.
914 self._cache = None
924 self._cache = None
915 # Maps rev to chain base rev.
925 # Maps rev to chain base rev.
916 self._chainbasecache = util.lrucachedict(100)
926 self._chainbasecache = util.lrucachedict(100)
917 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
927 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
918 self._chunkcache = (0, '')
928 self._chunkcache = (0, '')
919 # How much data to read and cache into the raw revlog data cache.
929 # How much data to read and cache into the raw revlog data cache.
920 self._chunkcachesize = 65536
930 self._chunkcachesize = 65536
921 self._maxchainlen = None
931 self._maxchainlen = None
922 self._deltabothparents = True
932 self._deltabothparents = True
923 self.index = []
933 self.index = []
924 # Mapping of partial identifiers to full nodes.
934 # Mapping of partial identifiers to full nodes.
925 self._pcache = {}
935 self._pcache = {}
926 # Mapping of revision integer to full node.
936 # Mapping of revision integer to full node.
927 self._nodecache = {nullid: nullrev}
937 self._nodecache = {nullid: nullrev}
928 self._nodepos = None
938 self._nodepos = None
929 self._compengine = 'zlib'
939 self._compengine = 'zlib'
930 self._maxdeltachainspan = -1
940 self._maxdeltachainspan = -1
931 self._withsparseread = False
941 self._withsparseread = False
932 self._sparserevlog = False
942 self._sparserevlog = False
933 self._srdensitythreshold = 0.50
943 self._srdensitythreshold = 0.50
934 self._srmingapsize = 262144
944 self._srmingapsize = 262144
935
945
936 mmapindexthreshold = None
946 mmapindexthreshold = None
937 v = REVLOG_DEFAULT_VERSION
947 v = REVLOG_DEFAULT_VERSION
938 opts = getattr(opener, 'options', None)
948 opts = getattr(opener, 'options', None)
939 if opts is not None:
949 if opts is not None:
940 if 'revlogv2' in opts:
950 if 'revlogv2' in opts:
941 # version 2 revlogs always use generaldelta.
951 # version 2 revlogs always use generaldelta.
942 v = REVLOGV2 | FLAG_GENERALDELTA | FLAG_INLINE_DATA
952 v = REVLOGV2 | FLAG_GENERALDELTA | FLAG_INLINE_DATA
943 elif 'revlogv1' in opts:
953 elif 'revlogv1' in opts:
944 if 'generaldelta' in opts:
954 if 'generaldelta' in opts:
945 v |= FLAG_GENERALDELTA
955 v |= FLAG_GENERALDELTA
946 else:
956 else:
947 v = 0
957 v = 0
948 if 'chunkcachesize' in opts:
958 if 'chunkcachesize' in opts:
949 self._chunkcachesize = opts['chunkcachesize']
959 self._chunkcachesize = opts['chunkcachesize']
950 if 'maxchainlen' in opts:
960 if 'maxchainlen' in opts:
951 self._maxchainlen = opts['maxchainlen']
961 self._maxchainlen = opts['maxchainlen']
952 if 'deltabothparents' in opts:
962 if 'deltabothparents' in opts:
953 self._deltabothparents = opts['deltabothparents']
963 self._deltabothparents = opts['deltabothparents']
954 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
964 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
955 if 'compengine' in opts:
965 if 'compengine' in opts:
956 self._compengine = opts['compengine']
966 self._compengine = opts['compengine']
957 if 'maxdeltachainspan' in opts:
967 if 'maxdeltachainspan' in opts:
958 self._maxdeltachainspan = opts['maxdeltachainspan']
968 self._maxdeltachainspan = opts['maxdeltachainspan']
959 if mmaplargeindex and 'mmapindexthreshold' in opts:
969 if mmaplargeindex and 'mmapindexthreshold' in opts:
960 mmapindexthreshold = opts['mmapindexthreshold']
970 mmapindexthreshold = opts['mmapindexthreshold']
961 self._sparserevlog = bool(opts.get('sparse-revlog', False))
971 self._sparserevlog = bool(opts.get('sparse-revlog', False))
962 withsparseread = bool(opts.get('with-sparse-read', False))
972 withsparseread = bool(opts.get('with-sparse-read', False))
963 # sparse-revlog forces sparse-read
973 # sparse-revlog forces sparse-read
964 self._withsparseread = self._sparserevlog or withsparseread
974 self._withsparseread = self._sparserevlog or withsparseread
965 if 'sparse-read-density-threshold' in opts:
975 if 'sparse-read-density-threshold' in opts:
966 self._srdensitythreshold = opts['sparse-read-density-threshold']
976 self._srdensitythreshold = opts['sparse-read-density-threshold']
967 if 'sparse-read-min-gap-size' in opts:
977 if 'sparse-read-min-gap-size' in opts:
968 self._srmingapsize = opts['sparse-read-min-gap-size']
978 self._srmingapsize = opts['sparse-read-min-gap-size']
969
979
970 if self._chunkcachesize <= 0:
980 if self._chunkcachesize <= 0:
971 raise RevlogError(_('revlog chunk cache size %r is not greater '
981 raise RevlogError(_('revlog chunk cache size %r is not greater '
972 'than 0') % self._chunkcachesize)
982 'than 0') % self._chunkcachesize)
973 elif self._chunkcachesize & (self._chunkcachesize - 1):
983 elif self._chunkcachesize & (self._chunkcachesize - 1):
974 raise RevlogError(_('revlog chunk cache size %r is not a power '
984 raise RevlogError(_('revlog chunk cache size %r is not a power '
975 'of 2') % self._chunkcachesize)
985 'of 2') % self._chunkcachesize)
976
986
977 indexdata = ''
987 indexdata = ''
978 self._initempty = True
988 self._initempty = True
979 try:
989 try:
980 with self._indexfp() as f:
990 with self._indexfp() as f:
981 if (mmapindexthreshold is not None and
991 if (mmapindexthreshold is not None and
982 self.opener.fstat(f).st_size >= mmapindexthreshold):
992 self.opener.fstat(f).st_size >= mmapindexthreshold):
983 indexdata = util.buffer(util.mmapread(f))
993 indexdata = util.buffer(util.mmapread(f))
984 else:
994 else:
985 indexdata = f.read()
995 indexdata = f.read()
986 if len(indexdata) > 0:
996 if len(indexdata) > 0:
987 v = versionformat_unpack(indexdata[:4])[0]
997 v = versionformat_unpack(indexdata[:4])[0]
988 self._initempty = False
998 self._initempty = False
989 except IOError as inst:
999 except IOError as inst:
990 if inst.errno != errno.ENOENT:
1000 if inst.errno != errno.ENOENT:
991 raise
1001 raise
992
1002
993 self.version = v
1003 self.version = v
994 self._inline = v & FLAG_INLINE_DATA
1004 self._inline = v & FLAG_INLINE_DATA
995 self._generaldelta = v & FLAG_GENERALDELTA
1005 self._generaldelta = v & FLAG_GENERALDELTA
996 flags = v & ~0xFFFF
1006 flags = v & ~0xFFFF
997 fmt = v & 0xFFFF
1007 fmt = v & 0xFFFF
998 if fmt == REVLOGV0:
1008 if fmt == REVLOGV0:
999 if flags:
1009 if flags:
1000 raise RevlogError(_('unknown flags (%#04x) in version %d '
1010 raise RevlogError(_('unknown flags (%#04x) in version %d '
1001 'revlog %s') %
1011 'revlog %s') %
1002 (flags >> 16, fmt, self.indexfile))
1012 (flags >> 16, fmt, self.indexfile))
1003 elif fmt == REVLOGV1:
1013 elif fmt == REVLOGV1:
1004 if flags & ~REVLOGV1_FLAGS:
1014 if flags & ~REVLOGV1_FLAGS:
1005 raise RevlogError(_('unknown flags (%#04x) in version %d '
1015 raise RevlogError(_('unknown flags (%#04x) in version %d '
1006 'revlog %s') %
1016 'revlog %s') %
1007 (flags >> 16, fmt, self.indexfile))
1017 (flags >> 16, fmt, self.indexfile))
1008 elif fmt == REVLOGV2:
1018 elif fmt == REVLOGV2:
1009 if flags & ~REVLOGV2_FLAGS:
1019 if flags & ~REVLOGV2_FLAGS:
1010 raise RevlogError(_('unknown flags (%#04x) in version %d '
1020 raise RevlogError(_('unknown flags (%#04x) in version %d '
1011 'revlog %s') %
1021 'revlog %s') %
1012 (flags >> 16, fmt, self.indexfile))
1022 (flags >> 16, fmt, self.indexfile))
1013 else:
1023 else:
1014 raise RevlogError(_('unknown version (%d) in revlog %s') %
1024 raise RevlogError(_('unknown version (%d) in revlog %s') %
1015 (fmt, self.indexfile))
1025 (fmt, self.indexfile))
1016
1026
1017 self.storedeltachains = True
1027 self.storedeltachains = True
1018
1028
1019 self._io = revlogio()
1029 self._io = revlogio()
1020 if self.version == REVLOGV0:
1030 if self.version == REVLOGV0:
1021 self._io = revlogoldio()
1031 self._io = revlogoldio()
1022 try:
1032 try:
1023 d = self._io.parseindex(indexdata, self._inline)
1033 d = self._io.parseindex(indexdata, self._inline)
1024 except (ValueError, IndexError):
1034 except (ValueError, IndexError):
1025 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
1035 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
1026 self.index, nodemap, self._chunkcache = d
1036 self.index, nodemap, self._chunkcache = d
1027 if nodemap is not None:
1037 if nodemap is not None:
1028 self.nodemap = self._nodecache = nodemap
1038 self.nodemap = self._nodecache = nodemap
1029 if not self._chunkcache:
1039 if not self._chunkcache:
1030 self._chunkclear()
1040 self._chunkclear()
1031 # revnum -> (chain-length, sum-delta-length)
1041 # revnum -> (chain-length, sum-delta-length)
1032 self._chaininfocache = {}
1042 self._chaininfocache = {}
1033 # revlog header -> revlog compressor
1043 # revlog header -> revlog compressor
1034 self._decompressors = {}
1044 self._decompressors = {}
1035
1045
1036 @util.propertycache
1046 @util.propertycache
1037 def _compressor(self):
1047 def _compressor(self):
1038 return util.compengines[self._compengine].revlogcompressor()
1048 return util.compengines[self._compengine].revlogcompressor()
1039
1049
1040 def _indexfp(self, mode='r'):
1050 def _indexfp(self, mode='r'):
1041 """file object for the revlog's index file"""
1051 """file object for the revlog's index file"""
1042 args = {r'mode': mode}
1052 args = {r'mode': mode}
1043 if mode != 'r':
1053 if mode != 'r':
1044 args[r'checkambig'] = self._checkambig
1054 args[r'checkambig'] = self._checkambig
1045 if mode == 'w':
1055 if mode == 'w':
1046 args[r'atomictemp'] = True
1056 args[r'atomictemp'] = True
1047 return self.opener(self.indexfile, **args)
1057 return self.opener(self.indexfile, **args)
1048
1058
1049 def _datafp(self, mode='r'):
1059 def _datafp(self, mode='r'):
1050 """file object for the revlog's data file"""
1060 """file object for the revlog's data file"""
1051 return self.opener(self.datafile, mode=mode)
1061 return self.opener(self.datafile, mode=mode)
1052
1062
1053 @contextlib.contextmanager
1063 @contextlib.contextmanager
1054 def _datareadfp(self, existingfp=None):
1064 def _datareadfp(self, existingfp=None):
1055 """file object suitable to read data"""
1065 """file object suitable to read data"""
1056 if existingfp is not None:
1066 if existingfp is not None:
1057 yield existingfp
1067 yield existingfp
1058 else:
1068 else:
1059 if self._inline:
1069 if self._inline:
1060 func = self._indexfp
1070 func = self._indexfp
1061 else:
1071 else:
1062 func = self._datafp
1072 func = self._datafp
1063 with func() as fp:
1073 with func() as fp:
1064 yield fp
1074 yield fp
1065
1075
1066 def tip(self):
1076 def tip(self):
1067 return self.node(len(self.index) - 1)
1077 return self.node(len(self.index) - 1)
1068 def __contains__(self, rev):
1078 def __contains__(self, rev):
1069 return 0 <= rev < len(self)
1079 return 0 <= rev < len(self)
1070 def __len__(self):
1080 def __len__(self):
1071 return len(self.index)
1081 return len(self.index)
1072 def __iter__(self):
1082 def __iter__(self):
1073 return iter(pycompat.xrange(len(self)))
1083 return iter(pycompat.xrange(len(self)))
1074 def revs(self, start=0, stop=None):
1084 def revs(self, start=0, stop=None):
1075 """iterate over all rev in this revlog (from start to stop)"""
1085 """iterate over all rev in this revlog (from start to stop)"""
1076 step = 1
1086 step = 1
1077 length = len(self)
1087 length = len(self)
1078 if stop is not None:
1088 if stop is not None:
1079 if start > stop:
1089 if start > stop:
1080 step = -1
1090 step = -1
1081 stop += step
1091 stop += step
1082 if stop > length:
1092 if stop > length:
1083 stop = length
1093 stop = length
1084 else:
1094 else:
1085 stop = length
1095 stop = length
1086 return pycompat.xrange(start, stop, step)
1096 return pycompat.xrange(start, stop, step)
1087
1097
1088 @util.propertycache
1098 @util.propertycache
1089 def nodemap(self):
1099 def nodemap(self):
1090 self.rev(self.node(0))
1100 self.rev(self.node(0))
1091 return self._nodecache
1101 return self._nodecache
1092
1102
1093 def hasnode(self, node):
1103 def hasnode(self, node):
1094 try:
1104 try:
1095 self.rev(node)
1105 self.rev(node)
1096 return True
1106 return True
1097 except KeyError:
1107 except KeyError:
1098 return False
1108 return False
1099
1109
1100 def candelta(self, baserev, rev):
1110 def candelta(self, baserev, rev):
1101 """whether two revisions (baserev, rev) can be delta-ed or not"""
1111 """whether two revisions (baserev, rev) can be delta-ed or not"""
1102 # Disable delta if either rev requires a content-changing flag
1112 # Disable delta if either rev requires a content-changing flag
1103 # processor (ex. LFS). This is because such flag processor can alter
1113 # processor (ex. LFS). This is because such flag processor can alter
1104 # the rawtext content that the delta will be based on, and two clients
1114 # the rawtext content that the delta will be based on, and two clients
1105 # could have a same revlog node with different flags (i.e. different
1115 # could have a same revlog node with different flags (i.e. different
1106 # rawtext contents) and the delta could be incompatible.
1116 # rawtext contents) and the delta could be incompatible.
1107 if ((self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS)
1117 if ((self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS)
1108 or (self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS)):
1118 or (self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS)):
1109 return False
1119 return False
1110 return True
1120 return True
1111
1121
1112 def clearcaches(self):
1122 def clearcaches(self):
1113 self._cache = None
1123 self._cache = None
1114 self._chainbasecache.clear()
1124 self._chainbasecache.clear()
1115 self._chunkcache = (0, '')
1125 self._chunkcache = (0, '')
1116 self._pcache = {}
1126 self._pcache = {}
1117
1127
1118 try:
1128 try:
1119 self._nodecache.clearcaches()
1129 self._nodecache.clearcaches()
1120 except AttributeError:
1130 except AttributeError:
1121 self._nodecache = {nullid: nullrev}
1131 self._nodecache = {nullid: nullrev}
1122 self._nodepos = None
1132 self._nodepos = None
1123
1133
1124 def rev(self, node):
1134 def rev(self, node):
1125 try:
1135 try:
1126 return self._nodecache[node]
1136 return self._nodecache[node]
1127 except TypeError:
1137 except TypeError:
1128 raise
1138 raise
1129 except RevlogError:
1139 except RevlogError:
1130 # parsers.c radix tree lookup failed
1140 # parsers.c radix tree lookup failed
1131 if node == wdirid or node in wdirfilenodeids:
1141 if node == wdirid or node in wdirfilenodeids:
1132 raise error.WdirUnsupported
1142 raise error.WdirUnsupported
1133 raise LookupError(node, self.indexfile, _('no node'))
1143 raise LookupError(node, self.indexfile, _('no node'))
1134 except KeyError:
1144 except KeyError:
1135 # pure python cache lookup failed
1145 # pure python cache lookup failed
1136 n = self._nodecache
1146 n = self._nodecache
1137 i = self.index
1147 i = self.index
1138 p = self._nodepos
1148 p = self._nodepos
1139 if p is None:
1149 if p is None:
1140 p = len(i) - 1
1150 p = len(i) - 1
1141 else:
1151 else:
1142 assert p < len(i)
1152 assert p < len(i)
1143 for r in pycompat.xrange(p, -1, -1):
1153 for r in pycompat.xrange(p, -1, -1):
1144 v = i[r][7]
1154 v = i[r][7]
1145 n[v] = r
1155 n[v] = r
1146 if v == node:
1156 if v == node:
1147 self._nodepos = r - 1
1157 self._nodepos = r - 1
1148 return r
1158 return r
1149 if node == wdirid or node in wdirfilenodeids:
1159 if node == wdirid or node in wdirfilenodeids:
1150 raise error.WdirUnsupported
1160 raise error.WdirUnsupported
1151 raise LookupError(node, self.indexfile, _('no node'))
1161 raise LookupError(node, self.indexfile, _('no node'))
1152
1162
1153 # Accessors for index entries.
1163 # Accessors for index entries.
1154
1164
1155 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
1165 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
1156 # are flags.
1166 # are flags.
1157 def start(self, rev):
1167 def start(self, rev):
1158 return int(self.index[rev][0] >> 16)
1168 return int(self.index[rev][0] >> 16)
1159
1169
1160 def flags(self, rev):
1170 def flags(self, rev):
1161 return self.index[rev][0] & 0xFFFF
1171 return self.index[rev][0] & 0xFFFF
1162
1172
1163 def length(self, rev):
1173 def length(self, rev):
1164 return self.index[rev][1]
1174 return self.index[rev][1]
1165
1175
1166 def rawsize(self, rev):
1176 def rawsize(self, rev):
1167 """return the length of the uncompressed text for a given revision"""
1177 """return the length of the uncompressed text for a given revision"""
1168 l = self.index[rev][2]
1178 l = self.index[rev][2]
1169 if l >= 0:
1179 if l >= 0:
1170 return l
1180 return l
1171
1181
1172 t = self.revision(rev, raw=True)
1182 t = self.revision(rev, raw=True)
1173 return len(t)
1183 return len(t)
1174
1184
1175 def size(self, rev):
1185 def size(self, rev):
1176 """length of non-raw text (processed by a "read" flag processor)"""
1186 """length of non-raw text (processed by a "read" flag processor)"""
1177 # fast path: if no "read" flag processor could change the content,
1187 # fast path: if no "read" flag processor could change the content,
1178 # size is rawsize. note: ELLIPSIS is known to not change the content.
1188 # size is rawsize. note: ELLIPSIS is known to not change the content.
1179 flags = self.flags(rev)
1189 flags = self.flags(rev)
1180 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
1190 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
1181 return self.rawsize(rev)
1191 return self.rawsize(rev)
1182
1192
1183 return len(self.revision(rev, raw=False))
1193 return len(self.revision(rev, raw=False))
1184
1194
1185 def chainbase(self, rev):
1195 def chainbase(self, rev):
1186 base = self._chainbasecache.get(rev)
1196 base = self._chainbasecache.get(rev)
1187 if base is not None:
1197 if base is not None:
1188 return base
1198 return base
1189
1199
1190 index = self.index
1200 index = self.index
1191 iterrev = rev
1201 iterrev = rev
1192 base = index[iterrev][3]
1202 base = index[iterrev][3]
1193 while base != iterrev:
1203 while base != iterrev:
1194 iterrev = base
1204 iterrev = base
1195 base = index[iterrev][3]
1205 base = index[iterrev][3]
1196
1206
1197 self._chainbasecache[rev] = base
1207 self._chainbasecache[rev] = base
1198 return base
1208 return base
1199
1209
1200 def linkrev(self, rev):
1210 def linkrev(self, rev):
1201 return self.index[rev][4]
1211 return self.index[rev][4]
1202
1212
1203 def parentrevs(self, rev):
1213 def parentrevs(self, rev):
1204 try:
1214 try:
1205 entry = self.index[rev]
1215 entry = self.index[rev]
1206 except IndexError:
1216 except IndexError:
1207 if rev == wdirrev:
1217 if rev == wdirrev:
1208 raise error.WdirUnsupported
1218 raise error.WdirUnsupported
1209 raise
1219 raise
1210
1220
1211 return entry[5], entry[6]
1221 return entry[5], entry[6]
1212
1222
1213 def node(self, rev):
1223 def node(self, rev):
1214 try:
1224 try:
1215 return self.index[rev][7]
1225 return self.index[rev][7]
1216 except IndexError:
1226 except IndexError:
1217 if rev == wdirrev:
1227 if rev == wdirrev:
1218 raise error.WdirUnsupported
1228 raise error.WdirUnsupported
1219 raise
1229 raise
1220
1230
1221 # Derived from index values.
1231 # Derived from index values.
1222
1232
1223 def end(self, rev):
1233 def end(self, rev):
1224 return self.start(rev) + self.length(rev)
1234 return self.start(rev) + self.length(rev)
1225
1235
1226 def parents(self, node):
1236 def parents(self, node):
1227 i = self.index
1237 i = self.index
1228 d = i[self.rev(node)]
1238 d = i[self.rev(node)]
1229 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
1239 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
1230
1240
1231 def chainlen(self, rev):
1241 def chainlen(self, rev):
1232 return self._chaininfo(rev)[0]
1242 return self._chaininfo(rev)[0]
1233
1243
1234 def _chaininfo(self, rev):
1244 def _chaininfo(self, rev):
1235 chaininfocache = self._chaininfocache
1245 chaininfocache = self._chaininfocache
1236 if rev in chaininfocache:
1246 if rev in chaininfocache:
1237 return chaininfocache[rev]
1247 return chaininfocache[rev]
1238 index = self.index
1248 index = self.index
1239 generaldelta = self._generaldelta
1249 generaldelta = self._generaldelta
1240 iterrev = rev
1250 iterrev = rev
1241 e = index[iterrev]
1251 e = index[iterrev]
1242 clen = 0
1252 clen = 0
1243 compresseddeltalen = 0
1253 compresseddeltalen = 0
1244 while iterrev != e[3]:
1254 while iterrev != e[3]:
1245 clen += 1
1255 clen += 1
1246 compresseddeltalen += e[1]
1256 compresseddeltalen += e[1]
1247 if generaldelta:
1257 if generaldelta:
1248 iterrev = e[3]
1258 iterrev = e[3]
1249 else:
1259 else:
1250 iterrev -= 1
1260 iterrev -= 1
1251 if iterrev in chaininfocache:
1261 if iterrev in chaininfocache:
1252 t = chaininfocache[iterrev]
1262 t = chaininfocache[iterrev]
1253 clen += t[0]
1263 clen += t[0]
1254 compresseddeltalen += t[1]
1264 compresseddeltalen += t[1]
1255 break
1265 break
1256 e = index[iterrev]
1266 e = index[iterrev]
1257 else:
1267 else:
1258 # Add text length of base since decompressing that also takes
1268 # Add text length of base since decompressing that also takes
1259 # work. For cache hits the length is already included.
1269 # work. For cache hits the length is already included.
1260 compresseddeltalen += e[1]
1270 compresseddeltalen += e[1]
1261 r = (clen, compresseddeltalen)
1271 r = (clen, compresseddeltalen)
1262 chaininfocache[rev] = r
1272 chaininfocache[rev] = r
1263 return r
1273 return r
1264
1274
1265 def _deltachain(self, rev, stoprev=None):
1275 def _deltachain(self, rev, stoprev=None):
1266 """Obtain the delta chain for a revision.
1276 """Obtain the delta chain for a revision.
1267
1277
1268 ``stoprev`` specifies a revision to stop at. If not specified, we
1278 ``stoprev`` specifies a revision to stop at. If not specified, we
1269 stop at the base of the chain.
1279 stop at the base of the chain.
1270
1280
1271 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
1281 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
1272 revs in ascending order and ``stopped`` is a bool indicating whether
1282 revs in ascending order and ``stopped`` is a bool indicating whether
1273 ``stoprev`` was hit.
1283 ``stoprev`` was hit.
1274 """
1284 """
1275 # Try C implementation.
1285 # Try C implementation.
1276 try:
1286 try:
1277 return self.index.deltachain(rev, stoprev, self._generaldelta)
1287 return self.index.deltachain(rev, stoprev, self._generaldelta)
1278 except AttributeError:
1288 except AttributeError:
1279 pass
1289 pass
1280
1290
1281 chain = []
1291 chain = []
1282
1292
1283 # Alias to prevent attribute lookup in tight loop.
1293 # Alias to prevent attribute lookup in tight loop.
1284 index = self.index
1294 index = self.index
1285 generaldelta = self._generaldelta
1295 generaldelta = self._generaldelta
1286
1296
1287 iterrev = rev
1297 iterrev = rev
1288 e = index[iterrev]
1298 e = index[iterrev]
1289 while iterrev != e[3] and iterrev != stoprev:
1299 while iterrev != e[3] and iterrev != stoprev:
1290 chain.append(iterrev)
1300 chain.append(iterrev)
1291 if generaldelta:
1301 if generaldelta:
1292 iterrev = e[3]
1302 iterrev = e[3]
1293 else:
1303 else:
1294 iterrev -= 1
1304 iterrev -= 1
1295 e = index[iterrev]
1305 e = index[iterrev]
1296
1306
1297 if iterrev == stoprev:
1307 if iterrev == stoprev:
1298 stopped = True
1308 stopped = True
1299 else:
1309 else:
1300 chain.append(iterrev)
1310 chain.append(iterrev)
1301 stopped = False
1311 stopped = False
1302
1312
1303 chain.reverse()
1313 chain.reverse()
1304 return chain, stopped
1314 return chain, stopped
1305
1315
1306 def ancestors(self, revs, stoprev=0, inclusive=False):
1316 def ancestors(self, revs, stoprev=0, inclusive=False):
1307 """Generate the ancestors of 'revs' in reverse topological order.
1317 """Generate the ancestors of 'revs' in reverse topological order.
1308 Does not generate revs lower than stoprev.
1318 Does not generate revs lower than stoprev.
1309
1319
1310 See the documentation for ancestor.lazyancestors for more details."""
1320 See the documentation for ancestor.lazyancestors for more details."""
1311
1321
1312 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
1322 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
1313 inclusive=inclusive)
1323 inclusive=inclusive)
1314
1324
1315 def descendants(self, revs):
1325 def descendants(self, revs):
1316 """Generate the descendants of 'revs' in revision order.
1326 """Generate the descendants of 'revs' in revision order.
1317
1327
1318 Yield a sequence of revision numbers starting with a child of
1328 Yield a sequence of revision numbers starting with a child of
1319 some rev in revs, i.e., each revision is *not* considered a
1329 some rev in revs, i.e., each revision is *not* considered a
1320 descendant of itself. Results are ordered by revision number (a
1330 descendant of itself. Results are ordered by revision number (a
1321 topological sort)."""
1331 topological sort)."""
1322 first = min(revs)
1332 first = min(revs)
1323 if first == nullrev:
1333 if first == nullrev:
1324 for i in self:
1334 for i in self:
1325 yield i
1335 yield i
1326 return
1336 return
1327
1337
1328 seen = set(revs)
1338 seen = set(revs)
1329 for i in self.revs(start=first + 1):
1339 for i in self.revs(start=first + 1):
1330 for x in self.parentrevs(i):
1340 for x in self.parentrevs(i):
1331 if x != nullrev and x in seen:
1341 if x != nullrev and x in seen:
1332 seen.add(i)
1342 seen.add(i)
1333 yield i
1343 yield i
1334 break
1344 break
1335
1345
1336 def findcommonmissing(self, common=None, heads=None):
1346 def findcommonmissing(self, common=None, heads=None):
1337 """Return a tuple of the ancestors of common and the ancestors of heads
1347 """Return a tuple of the ancestors of common and the ancestors of heads
1338 that are not ancestors of common. In revset terminology, we return the
1348 that are not ancestors of common. In revset terminology, we return the
1339 tuple:
1349 tuple:
1340
1350
1341 ::common, (::heads) - (::common)
1351 ::common, (::heads) - (::common)
1342
1352
1343 The list is sorted by revision number, meaning it is
1353 The list is sorted by revision number, meaning it is
1344 topologically sorted.
1354 topologically sorted.
1345
1355
1346 'heads' and 'common' are both lists of node IDs. If heads is
1356 'heads' and 'common' are both lists of node IDs. If heads is
1347 not supplied, uses all of the revlog's heads. If common is not
1357 not supplied, uses all of the revlog's heads. If common is not
1348 supplied, uses nullid."""
1358 supplied, uses nullid."""
1349 if common is None:
1359 if common is None:
1350 common = [nullid]
1360 common = [nullid]
1351 if heads is None:
1361 if heads is None:
1352 heads = self.heads()
1362 heads = self.heads()
1353
1363
1354 common = [self.rev(n) for n in common]
1364 common = [self.rev(n) for n in common]
1355 heads = [self.rev(n) for n in heads]
1365 heads = [self.rev(n) for n in heads]
1356
1366
1357 # we want the ancestors, but inclusive
1367 # we want the ancestors, but inclusive
1358 class lazyset(object):
1368 class lazyset(object):
1359 def __init__(self, lazyvalues):
1369 def __init__(self, lazyvalues):
1360 self.addedvalues = set()
1370 self.addedvalues = set()
1361 self.lazyvalues = lazyvalues
1371 self.lazyvalues = lazyvalues
1362
1372
1363 def __contains__(self, value):
1373 def __contains__(self, value):
1364 return value in self.addedvalues or value in self.lazyvalues
1374 return value in self.addedvalues or value in self.lazyvalues
1365
1375
1366 def __iter__(self):
1376 def __iter__(self):
1367 added = self.addedvalues
1377 added = self.addedvalues
1368 for r in added:
1378 for r in added:
1369 yield r
1379 yield r
1370 for r in self.lazyvalues:
1380 for r in self.lazyvalues:
1371 if not r in added:
1381 if not r in added:
1372 yield r
1382 yield r
1373
1383
1374 def add(self, value):
1384 def add(self, value):
1375 self.addedvalues.add(value)
1385 self.addedvalues.add(value)
1376
1386
1377 def update(self, values):
1387 def update(self, values):
1378 self.addedvalues.update(values)
1388 self.addedvalues.update(values)
1379
1389
1380 has = lazyset(self.ancestors(common))
1390 has = lazyset(self.ancestors(common))
1381 has.add(nullrev)
1391 has.add(nullrev)
1382 has.update(common)
1392 has.update(common)
1383
1393
1384 # take all ancestors from heads that aren't in has
1394 # take all ancestors from heads that aren't in has
1385 missing = set()
1395 missing = set()
1386 visit = collections.deque(r for r in heads if r not in has)
1396 visit = collections.deque(r for r in heads if r not in has)
1387 while visit:
1397 while visit:
1388 r = visit.popleft()
1398 r = visit.popleft()
1389 if r in missing:
1399 if r in missing:
1390 continue
1400 continue
1391 else:
1401 else:
1392 missing.add(r)
1402 missing.add(r)
1393 for p in self.parentrevs(r):
1403 for p in self.parentrevs(r):
1394 if p not in has:
1404 if p not in has:
1395 visit.append(p)
1405 visit.append(p)
1396 missing = list(missing)
1406 missing = list(missing)
1397 missing.sort()
1407 missing.sort()
1398 return has, [self.node(miss) for miss in missing]
1408 return has, [self.node(miss) for miss in missing]
1399
1409
1400 def incrementalmissingrevs(self, common=None):
1410 def incrementalmissingrevs(self, common=None):
1401 """Return an object that can be used to incrementally compute the
1411 """Return an object that can be used to incrementally compute the
1402 revision numbers of the ancestors of arbitrary sets that are not
1412 revision numbers of the ancestors of arbitrary sets that are not
1403 ancestors of common. This is an ancestor.incrementalmissingancestors
1413 ancestors of common. This is an ancestor.incrementalmissingancestors
1404 object.
1414 object.
1405
1415
1406 'common' is a list of revision numbers. If common is not supplied, uses
1416 'common' is a list of revision numbers. If common is not supplied, uses
1407 nullrev.
1417 nullrev.
1408 """
1418 """
1409 if common is None:
1419 if common is None:
1410 common = [nullrev]
1420 common = [nullrev]
1411
1421
1412 return ancestor.incrementalmissingancestors(self.parentrevs, common)
1422 return ancestor.incrementalmissingancestors(self.parentrevs, common)
1413
1423
1414 def findmissingrevs(self, common=None, heads=None):
1424 def findmissingrevs(self, common=None, heads=None):
1415 """Return the revision numbers of the ancestors of heads that
1425 """Return the revision numbers of the ancestors of heads that
1416 are not ancestors of common.
1426 are not ancestors of common.
1417
1427
1418 More specifically, return a list of revision numbers corresponding to
1428 More specifically, return a list of revision numbers corresponding to
1419 nodes N such that every N satisfies the following constraints:
1429 nodes N such that every N satisfies the following constraints:
1420
1430
1421 1. N is an ancestor of some node in 'heads'
1431 1. N is an ancestor of some node in 'heads'
1422 2. N is not an ancestor of any node in 'common'
1432 2. N is not an ancestor of any node in 'common'
1423
1433
1424 The list is sorted by revision number, meaning it is
1434 The list is sorted by revision number, meaning it is
1425 topologically sorted.
1435 topologically sorted.
1426
1436
1427 'heads' and 'common' are both lists of revision numbers. If heads is
1437 'heads' and 'common' are both lists of revision numbers. If heads is
1428 not supplied, uses all of the revlog's heads. If common is not
1438 not supplied, uses all of the revlog's heads. If common is not
1429 supplied, uses nullid."""
1439 supplied, uses nullid."""
1430 if common is None:
1440 if common is None:
1431 common = [nullrev]
1441 common = [nullrev]
1432 if heads is None:
1442 if heads is None:
1433 heads = self.headrevs()
1443 heads = self.headrevs()
1434
1444
1435 inc = self.incrementalmissingrevs(common=common)
1445 inc = self.incrementalmissingrevs(common=common)
1436 return inc.missingancestors(heads)
1446 return inc.missingancestors(heads)
1437
1447
1438 def findmissing(self, common=None, heads=None):
1448 def findmissing(self, common=None, heads=None):
1439 """Return the ancestors of heads that are not ancestors of common.
1449 """Return the ancestors of heads that are not ancestors of common.
1440
1450
1441 More specifically, return a list of nodes N such that every N
1451 More specifically, return a list of nodes N such that every N
1442 satisfies the following constraints:
1452 satisfies the following constraints:
1443
1453
1444 1. N is an ancestor of some node in 'heads'
1454 1. N is an ancestor of some node in 'heads'
1445 2. N is not an ancestor of any node in 'common'
1455 2. N is not an ancestor of any node in 'common'
1446
1456
1447 The list is sorted by revision number, meaning it is
1457 The list is sorted by revision number, meaning it is
1448 topologically sorted.
1458 topologically sorted.
1449
1459
1450 'heads' and 'common' are both lists of node IDs. If heads is
1460 'heads' and 'common' are both lists of node IDs. If heads is
1451 not supplied, uses all of the revlog's heads. If common is not
1461 not supplied, uses all of the revlog's heads. If common is not
1452 supplied, uses nullid."""
1462 supplied, uses nullid."""
1453 if common is None:
1463 if common is None:
1454 common = [nullid]
1464 common = [nullid]
1455 if heads is None:
1465 if heads is None:
1456 heads = self.heads()
1466 heads = self.heads()
1457
1467
1458 common = [self.rev(n) for n in common]
1468 common = [self.rev(n) for n in common]
1459 heads = [self.rev(n) for n in heads]
1469 heads = [self.rev(n) for n in heads]
1460
1470
1461 inc = self.incrementalmissingrevs(common=common)
1471 inc = self.incrementalmissingrevs(common=common)
1462 return [self.node(r) for r in inc.missingancestors(heads)]
1472 return [self.node(r) for r in inc.missingancestors(heads)]
1463
1473
1464 def nodesbetween(self, roots=None, heads=None):
1474 def nodesbetween(self, roots=None, heads=None):
1465 """Return a topological path from 'roots' to 'heads'.
1475 """Return a topological path from 'roots' to 'heads'.
1466
1476
1467 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
1477 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
1468 topologically sorted list of all nodes N that satisfy both of
1478 topologically sorted list of all nodes N that satisfy both of
1469 these constraints:
1479 these constraints:
1470
1480
1471 1. N is a descendant of some node in 'roots'
1481 1. N is a descendant of some node in 'roots'
1472 2. N is an ancestor of some node in 'heads'
1482 2. N is an ancestor of some node in 'heads'
1473
1483
1474 Every node is considered to be both a descendant and an ancestor
1484 Every node is considered to be both a descendant and an ancestor
1475 of itself, so every reachable node in 'roots' and 'heads' will be
1485 of itself, so every reachable node in 'roots' and 'heads' will be
1476 included in 'nodes'.
1486 included in 'nodes'.
1477
1487
1478 'outroots' is the list of reachable nodes in 'roots', i.e., the
1488 'outroots' is the list of reachable nodes in 'roots', i.e., the
1479 subset of 'roots' that is returned in 'nodes'. Likewise,
1489 subset of 'roots' that is returned in 'nodes'. Likewise,
1480 'outheads' is the subset of 'heads' that is also in 'nodes'.
1490 'outheads' is the subset of 'heads' that is also in 'nodes'.
1481
1491
1482 'roots' and 'heads' are both lists of node IDs. If 'roots' is
1492 'roots' and 'heads' are both lists of node IDs. If 'roots' is
1483 unspecified, uses nullid as the only root. If 'heads' is
1493 unspecified, uses nullid as the only root. If 'heads' is
1484 unspecified, uses list of all of the revlog's heads."""
1494 unspecified, uses list of all of the revlog's heads."""
1485 nonodes = ([], [], [])
1495 nonodes = ([], [], [])
1486 if roots is not None:
1496 if roots is not None:
1487 roots = list(roots)
1497 roots = list(roots)
1488 if not roots:
1498 if not roots:
1489 return nonodes
1499 return nonodes
1490 lowestrev = min([self.rev(n) for n in roots])
1500 lowestrev = min([self.rev(n) for n in roots])
1491 else:
1501 else:
1492 roots = [nullid] # Everybody's a descendant of nullid
1502 roots = [nullid] # Everybody's a descendant of nullid
1493 lowestrev = nullrev
1503 lowestrev = nullrev
1494 if (lowestrev == nullrev) and (heads is None):
1504 if (lowestrev == nullrev) and (heads is None):
1495 # We want _all_ the nodes!
1505 # We want _all_ the nodes!
1496 return ([self.node(r) for r in self], [nullid], list(self.heads()))
1506 return ([self.node(r) for r in self], [nullid], list(self.heads()))
1497 if heads is None:
1507 if heads is None:
1498 # All nodes are ancestors, so the latest ancestor is the last
1508 # All nodes are ancestors, so the latest ancestor is the last
1499 # node.
1509 # node.
1500 highestrev = len(self) - 1
1510 highestrev = len(self) - 1
1501 # Set ancestors to None to signal that every node is an ancestor.
1511 # Set ancestors to None to signal that every node is an ancestor.
1502 ancestors = None
1512 ancestors = None
1503 # Set heads to an empty dictionary for later discovery of heads
1513 # Set heads to an empty dictionary for later discovery of heads
1504 heads = {}
1514 heads = {}
1505 else:
1515 else:
1506 heads = list(heads)
1516 heads = list(heads)
1507 if not heads:
1517 if not heads:
1508 return nonodes
1518 return nonodes
1509 ancestors = set()
1519 ancestors = set()
1510 # Turn heads into a dictionary so we can remove 'fake' heads.
1520 # Turn heads into a dictionary so we can remove 'fake' heads.
1511 # Also, later we will be using it to filter out the heads we can't
1521 # Also, later we will be using it to filter out the heads we can't
1512 # find from roots.
1522 # find from roots.
1513 heads = dict.fromkeys(heads, False)
1523 heads = dict.fromkeys(heads, False)
1514 # Start at the top and keep marking parents until we're done.
1524 # Start at the top and keep marking parents until we're done.
1515 nodestotag = set(heads)
1525 nodestotag = set(heads)
1516 # Remember where the top was so we can use it as a limit later.
1526 # Remember where the top was so we can use it as a limit later.
1517 highestrev = max([self.rev(n) for n in nodestotag])
1527 highestrev = max([self.rev(n) for n in nodestotag])
1518 while nodestotag:
1528 while nodestotag:
1519 # grab a node to tag
1529 # grab a node to tag
1520 n = nodestotag.pop()
1530 n = nodestotag.pop()
1521 # Never tag nullid
1531 # Never tag nullid
1522 if n == nullid:
1532 if n == nullid:
1523 continue
1533 continue
1524 # A node's revision number represents its place in a
1534 # A node's revision number represents its place in a
1525 # topologically sorted list of nodes.
1535 # topologically sorted list of nodes.
1526 r = self.rev(n)
1536 r = self.rev(n)
1527 if r >= lowestrev:
1537 if r >= lowestrev:
1528 if n not in ancestors:
1538 if n not in ancestors:
1529 # If we are possibly a descendant of one of the roots
1539 # If we are possibly a descendant of one of the roots
1530 # and we haven't already been marked as an ancestor
1540 # and we haven't already been marked as an ancestor
1531 ancestors.add(n) # Mark as ancestor
1541 ancestors.add(n) # Mark as ancestor
1532 # Add non-nullid parents to list of nodes to tag.
1542 # Add non-nullid parents to list of nodes to tag.
1533 nodestotag.update([p for p in self.parents(n) if
1543 nodestotag.update([p for p in self.parents(n) if
1534 p != nullid])
1544 p != nullid])
1535 elif n in heads: # We've seen it before, is it a fake head?
1545 elif n in heads: # We've seen it before, is it a fake head?
1536 # So it is, real heads should not be the ancestors of
1546 # So it is, real heads should not be the ancestors of
1537 # any other heads.
1547 # any other heads.
1538 heads.pop(n)
1548 heads.pop(n)
1539 if not ancestors:
1549 if not ancestors:
1540 return nonodes
1550 return nonodes
1541 # Now that we have our set of ancestors, we want to remove any
1551 # Now that we have our set of ancestors, we want to remove any
1542 # roots that are not ancestors.
1552 # roots that are not ancestors.
1543
1553
1544 # If one of the roots was nullid, everything is included anyway.
1554 # If one of the roots was nullid, everything is included anyway.
1545 if lowestrev > nullrev:
1555 if lowestrev > nullrev:
1546 # But, since we weren't, let's recompute the lowest rev to not
1556 # But, since we weren't, let's recompute the lowest rev to not
1547 # include roots that aren't ancestors.
1557 # include roots that aren't ancestors.
1548
1558
1549 # Filter out roots that aren't ancestors of heads
1559 # Filter out roots that aren't ancestors of heads
1550 roots = [root for root in roots if root in ancestors]
1560 roots = [root for root in roots if root in ancestors]
1551 # Recompute the lowest revision
1561 # Recompute the lowest revision
1552 if roots:
1562 if roots:
1553 lowestrev = min([self.rev(root) for root in roots])
1563 lowestrev = min([self.rev(root) for root in roots])
1554 else:
1564 else:
1555 # No more roots? Return empty list
1565 # No more roots? Return empty list
1556 return nonodes
1566 return nonodes
1557 else:
1567 else:
1558 # We are descending from nullid, and don't need to care about
1568 # We are descending from nullid, and don't need to care about
1559 # any other roots.
1569 # any other roots.
1560 lowestrev = nullrev
1570 lowestrev = nullrev
1561 roots = [nullid]
1571 roots = [nullid]
1562 # Transform our roots list into a set.
1572 # Transform our roots list into a set.
1563 descendants = set(roots)
1573 descendants = set(roots)
1564 # Also, keep the original roots so we can filter out roots that aren't
1574 # Also, keep the original roots so we can filter out roots that aren't
1565 # 'real' roots (i.e. are descended from other roots).
1575 # 'real' roots (i.e. are descended from other roots).
1566 roots = descendants.copy()
1576 roots = descendants.copy()
1567 # Our topologically sorted list of output nodes.
1577 # Our topologically sorted list of output nodes.
1568 orderedout = []
1578 orderedout = []
1569 # Don't start at nullid since we don't want nullid in our output list,
1579 # Don't start at nullid since we don't want nullid in our output list,
1570 # and if nullid shows up in descendants, empty parents will look like
1580 # and if nullid shows up in descendants, empty parents will look like
1571 # they're descendants.
1581 # they're descendants.
1572 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1582 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1573 n = self.node(r)
1583 n = self.node(r)
1574 isdescendant = False
1584 isdescendant = False
1575 if lowestrev == nullrev: # Everybody is a descendant of nullid
1585 if lowestrev == nullrev: # Everybody is a descendant of nullid
1576 isdescendant = True
1586 isdescendant = True
1577 elif n in descendants:
1587 elif n in descendants:
1578 # n is already a descendant
1588 # n is already a descendant
1579 isdescendant = True
1589 isdescendant = True
1580 # This check only needs to be done here because all the roots
1590 # This check only needs to be done here because all the roots
1581 # will start being marked is descendants before the loop.
1591 # will start being marked is descendants before the loop.
1582 if n in roots:
1592 if n in roots:
1583 # If n was a root, check if it's a 'real' root.
1593 # If n was a root, check if it's a 'real' root.
1584 p = tuple(self.parents(n))
1594 p = tuple(self.parents(n))
1585 # If any of its parents are descendants, it's not a root.
1595 # If any of its parents are descendants, it's not a root.
1586 if (p[0] in descendants) or (p[1] in descendants):
1596 if (p[0] in descendants) or (p[1] in descendants):
1587 roots.remove(n)
1597 roots.remove(n)
1588 else:
1598 else:
1589 p = tuple(self.parents(n))
1599 p = tuple(self.parents(n))
1590 # A node is a descendant if either of its parents are
1600 # A node is a descendant if either of its parents are
1591 # descendants. (We seeded the dependents list with the roots
1601 # descendants. (We seeded the dependents list with the roots
1592 # up there, remember?)
1602 # up there, remember?)
1593 if (p[0] in descendants) or (p[1] in descendants):
1603 if (p[0] in descendants) or (p[1] in descendants):
1594 descendants.add(n)
1604 descendants.add(n)
1595 isdescendant = True
1605 isdescendant = True
1596 if isdescendant and ((ancestors is None) or (n in ancestors)):
1606 if isdescendant and ((ancestors is None) or (n in ancestors)):
1597 # Only include nodes that are both descendants and ancestors.
1607 # Only include nodes that are both descendants and ancestors.
1598 orderedout.append(n)
1608 orderedout.append(n)
1599 if (ancestors is not None) and (n in heads):
1609 if (ancestors is not None) and (n in heads):
1600 # We're trying to figure out which heads are reachable
1610 # We're trying to figure out which heads are reachable
1601 # from roots.
1611 # from roots.
1602 # Mark this head as having been reached
1612 # Mark this head as having been reached
1603 heads[n] = True
1613 heads[n] = True
1604 elif ancestors is None:
1614 elif ancestors is None:
1605 # Otherwise, we're trying to discover the heads.
1615 # Otherwise, we're trying to discover the heads.
1606 # Assume this is a head because if it isn't, the next step
1616 # Assume this is a head because if it isn't, the next step
1607 # will eventually remove it.
1617 # will eventually remove it.
1608 heads[n] = True
1618 heads[n] = True
1609 # But, obviously its parents aren't.
1619 # But, obviously its parents aren't.
1610 for p in self.parents(n):
1620 for p in self.parents(n):
1611 heads.pop(p, None)
1621 heads.pop(p, None)
1612 heads = [head for head, flag in heads.iteritems() if flag]
1622 heads = [head for head, flag in heads.iteritems() if flag]
1613 roots = list(roots)
1623 roots = list(roots)
1614 assert orderedout
1624 assert orderedout
1615 assert roots
1625 assert roots
1616 assert heads
1626 assert heads
1617 return (orderedout, roots, heads)
1627 return (orderedout, roots, heads)
1618
1628
1619 def headrevs(self):
1629 def headrevs(self):
1620 try:
1630 try:
1621 return self.index.headrevs()
1631 return self.index.headrevs()
1622 except AttributeError:
1632 except AttributeError:
1623 return self._headrevs()
1633 return self._headrevs()
1624
1634
1625 def computephases(self, roots):
1635 def computephases(self, roots):
1626 return self.index.computephasesmapsets(roots)
1636 return self.index.computephasesmapsets(roots)
1627
1637
1628 def _headrevs(self):
1638 def _headrevs(self):
1629 count = len(self)
1639 count = len(self)
1630 if not count:
1640 if not count:
1631 return [nullrev]
1641 return [nullrev]
1632 # we won't iter over filtered rev so nobody is a head at start
1642 # we won't iter over filtered rev so nobody is a head at start
1633 ishead = [0] * (count + 1)
1643 ishead = [0] * (count + 1)
1634 index = self.index
1644 index = self.index
1635 for r in self:
1645 for r in self:
1636 ishead[r] = 1 # I may be an head
1646 ishead[r] = 1 # I may be an head
1637 e = index[r]
1647 e = index[r]
1638 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1648 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1639 return [r for r, val in enumerate(ishead) if val]
1649 return [r for r, val in enumerate(ishead) if val]
1640
1650
1641 def heads(self, start=None, stop=None):
1651 def heads(self, start=None, stop=None):
1642 """return the list of all nodes that have no children
1652 """return the list of all nodes that have no children
1643
1653
1644 if start is specified, only heads that are descendants of
1654 if start is specified, only heads that are descendants of
1645 start will be returned
1655 start will be returned
1646 if stop is specified, it will consider all the revs from stop
1656 if stop is specified, it will consider all the revs from stop
1647 as if they had no children
1657 as if they had no children
1648 """
1658 """
1649 if start is None and stop is None:
1659 if start is None and stop is None:
1650 if not len(self):
1660 if not len(self):
1651 return [nullid]
1661 return [nullid]
1652 return [self.node(r) for r in self.headrevs()]
1662 return [self.node(r) for r in self.headrevs()]
1653
1663
1654 if start is None:
1664 if start is None:
1655 start = nullid
1665 start = nullid
1656 if stop is None:
1666 if stop is None:
1657 stop = []
1667 stop = []
1658 stoprevs = set([self.rev(n) for n in stop])
1668 stoprevs = set([self.rev(n) for n in stop])
1659 startrev = self.rev(start)
1669 startrev = self.rev(start)
1660 reachable = {startrev}
1670 reachable = {startrev}
1661 heads = {startrev}
1671 heads = {startrev}
1662
1672
1663 parentrevs = self.parentrevs
1673 parentrevs = self.parentrevs
1664 for r in self.revs(start=startrev + 1):
1674 for r in self.revs(start=startrev + 1):
1665 for p in parentrevs(r):
1675 for p in parentrevs(r):
1666 if p in reachable:
1676 if p in reachable:
1667 if r not in stoprevs:
1677 if r not in stoprevs:
1668 reachable.add(r)
1678 reachable.add(r)
1669 heads.add(r)
1679 heads.add(r)
1670 if p in heads and p not in stoprevs:
1680 if p in heads and p not in stoprevs:
1671 heads.remove(p)
1681 heads.remove(p)
1672
1682
1673 return [self.node(r) for r in heads]
1683 return [self.node(r) for r in heads]
1674
1684
1675 def children(self, node):
1685 def children(self, node):
1676 """find the children of a given node"""
1686 """find the children of a given node"""
1677 c = []
1687 c = []
1678 p = self.rev(node)
1688 p = self.rev(node)
1679 for r in self.revs(start=p + 1):
1689 for r in self.revs(start=p + 1):
1680 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1690 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1681 if prevs:
1691 if prevs:
1682 for pr in prevs:
1692 for pr in prevs:
1683 if pr == p:
1693 if pr == p:
1684 c.append(self.node(r))
1694 c.append(self.node(r))
1685 elif p == nullrev:
1695 elif p == nullrev:
1686 c.append(self.node(r))
1696 c.append(self.node(r))
1687 return c
1697 return c
1688
1698
1689 def commonancestorsheads(self, a, b):
1699 def commonancestorsheads(self, a, b):
1690 """calculate all the heads of the common ancestors of nodes a and b"""
1700 """calculate all the heads of the common ancestors of nodes a and b"""
1691 a, b = self.rev(a), self.rev(b)
1701 a, b = self.rev(a), self.rev(b)
1692 ancs = self._commonancestorsheads(a, b)
1702 ancs = self._commonancestorsheads(a, b)
1693 return pycompat.maplist(self.node, ancs)
1703 return pycompat.maplist(self.node, ancs)
1694
1704
1695 def _commonancestorsheads(self, *revs):
1705 def _commonancestorsheads(self, *revs):
1696 """calculate all the heads of the common ancestors of revs"""
1706 """calculate all the heads of the common ancestors of revs"""
1697 try:
1707 try:
1698 ancs = self.index.commonancestorsheads(*revs)
1708 ancs = self.index.commonancestorsheads(*revs)
1699 except (AttributeError, OverflowError): # C implementation failed
1709 except (AttributeError, OverflowError): # C implementation failed
1700 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1710 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1701 return ancs
1711 return ancs
1702
1712
1703 def isancestor(self, a, b):
1713 def isancestor(self, a, b):
1704 """return True if node a is an ancestor of node b
1714 """return True if node a is an ancestor of node b
1705
1715
1706 A revision is considered an ancestor of itself."""
1716 A revision is considered an ancestor of itself."""
1707 a, b = self.rev(a), self.rev(b)
1717 a, b = self.rev(a), self.rev(b)
1708 return self.isancestorrev(a, b)
1718 return self.isancestorrev(a, b)
1709
1719
1710 def isancestorrev(self, a, b):
1720 def isancestorrev(self, a, b):
1711 """return True if revision a is an ancestor of revision b
1721 """return True if revision a is an ancestor of revision b
1712
1722
1713 A revision is considered an ancestor of itself.
1723 A revision is considered an ancestor of itself.
1714
1724
1715 The implementation of this is trivial but the use of
1725 The implementation of this is trivial but the use of
1716 commonancestorsheads is not."""
1726 commonancestorsheads is not."""
1717 if a == nullrev:
1727 if a == nullrev:
1718 return True
1728 return True
1719 elif a == b:
1729 elif a == b:
1720 return True
1730 return True
1721 elif a > b:
1731 elif a > b:
1722 return False
1732 return False
1723 return a in self._commonancestorsheads(a, b)
1733 return a in self._commonancestorsheads(a, b)
1724
1734
1725 def ancestor(self, a, b):
1735 def ancestor(self, a, b):
1726 """calculate the "best" common ancestor of nodes a and b"""
1736 """calculate the "best" common ancestor of nodes a and b"""
1727
1737
1728 a, b = self.rev(a), self.rev(b)
1738 a, b = self.rev(a), self.rev(b)
1729 try:
1739 try:
1730 ancs = self.index.ancestors(a, b)
1740 ancs = self.index.ancestors(a, b)
1731 except (AttributeError, OverflowError):
1741 except (AttributeError, OverflowError):
1732 ancs = ancestor.ancestors(self.parentrevs, a, b)
1742 ancs = ancestor.ancestors(self.parentrevs, a, b)
1733 if ancs:
1743 if ancs:
1734 # choose a consistent winner when there's a tie
1744 # choose a consistent winner when there's a tie
1735 return min(map(self.node, ancs))
1745 return min(map(self.node, ancs))
1736 return nullid
1746 return nullid
1737
1747
1738 def _match(self, id):
1748 def _match(self, id):
1739 if isinstance(id, int):
1749 if isinstance(id, int):
1740 # rev
1750 # rev
1741 return self.node(id)
1751 return self.node(id)
1742 if len(id) == 20:
1752 if len(id) == 20:
1743 # possibly a binary node
1753 # possibly a binary node
1744 # odds of a binary node being all hex in ASCII are 1 in 10**25
1754 # odds of a binary node being all hex in ASCII are 1 in 10**25
1745 try:
1755 try:
1746 node = id
1756 node = id
1747 self.rev(node) # quick search the index
1757 self.rev(node) # quick search the index
1748 return node
1758 return node
1749 except LookupError:
1759 except LookupError:
1750 pass # may be partial hex id
1760 pass # may be partial hex id
1751 try:
1761 try:
1752 # str(rev)
1762 # str(rev)
1753 rev = int(id)
1763 rev = int(id)
1754 if "%d" % rev != id:
1764 if "%d" % rev != id:
1755 raise ValueError
1765 raise ValueError
1756 if rev < 0:
1766 if rev < 0:
1757 rev = len(self) + rev
1767 rev = len(self) + rev
1758 if rev < 0 or rev >= len(self):
1768 if rev < 0 or rev >= len(self):
1759 raise ValueError
1769 raise ValueError
1760 return self.node(rev)
1770 return self.node(rev)
1761 except (ValueError, OverflowError):
1771 except (ValueError, OverflowError):
1762 pass
1772 pass
1763 if len(id) == 40:
1773 if len(id) == 40:
1764 try:
1774 try:
1765 # a full hex nodeid?
1775 # a full hex nodeid?
1766 node = bin(id)
1776 node = bin(id)
1767 self.rev(node)
1777 self.rev(node)
1768 return node
1778 return node
1769 except (TypeError, LookupError):
1779 except (TypeError, LookupError):
1770 pass
1780 pass
1771
1781
1772 def _partialmatch(self, id):
1782 def _partialmatch(self, id):
1773 # we don't care wdirfilenodeids as they should be always full hash
1783 # we don't care wdirfilenodeids as they should be always full hash
1774 maybewdir = wdirhex.startswith(id)
1784 maybewdir = wdirhex.startswith(id)
1775 try:
1785 try:
1776 partial = self.index.partialmatch(id)
1786 partial = self.index.partialmatch(id)
1777 if partial and self.hasnode(partial):
1787 if partial and self.hasnode(partial):
1778 if maybewdir:
1788 if maybewdir:
1779 # single 'ff...' match in radix tree, ambiguous with wdir
1789 # single 'ff...' match in radix tree, ambiguous with wdir
1780 raise RevlogError
1790 raise RevlogError
1781 return partial
1791 return partial
1782 if maybewdir:
1792 if maybewdir:
1783 # no 'ff...' match in radix tree, wdir identified
1793 # no 'ff...' match in radix tree, wdir identified
1784 raise error.WdirUnsupported
1794 raise error.WdirUnsupported
1785 return None
1795 return None
1786 except RevlogError:
1796 except RevlogError:
1787 # parsers.c radix tree lookup gave multiple matches
1797 # parsers.c radix tree lookup gave multiple matches
1788 # fast path: for unfiltered changelog, radix tree is accurate
1798 # fast path: for unfiltered changelog, radix tree is accurate
1789 if not getattr(self, 'filteredrevs', None):
1799 if not getattr(self, 'filteredrevs', None):
1790 raise AmbiguousPrefixLookupError(id, self.indexfile,
1800 raise AmbiguousPrefixLookupError(id, self.indexfile,
1791 _('ambiguous identifier'))
1801 _('ambiguous identifier'))
1792 # fall through to slow path that filters hidden revisions
1802 # fall through to slow path that filters hidden revisions
1793 except (AttributeError, ValueError):
1803 except (AttributeError, ValueError):
1794 # we are pure python, or key was too short to search radix tree
1804 # we are pure python, or key was too short to search radix tree
1795 pass
1805 pass
1796
1806
1797 if id in self._pcache:
1807 if id in self._pcache:
1798 return self._pcache[id]
1808 return self._pcache[id]
1799
1809
1800 if len(id) <= 40:
1810 if len(id) <= 40:
1801 try:
1811 try:
1802 # hex(node)[:...]
1812 # hex(node)[:...]
1803 l = len(id) // 2 # grab an even number of digits
1813 l = len(id) // 2 # grab an even number of digits
1804 prefix = bin(id[:l * 2])
1814 prefix = bin(id[:l * 2])
1805 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1815 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1806 nl = [n for n in nl if hex(n).startswith(id) and
1816 nl = [n for n in nl if hex(n).startswith(id) and
1807 self.hasnode(n)]
1817 self.hasnode(n)]
1808 if len(nl) > 0:
1818 if len(nl) > 0:
1809 if len(nl) == 1 and not maybewdir:
1819 if len(nl) == 1 and not maybewdir:
1810 self._pcache[id] = nl[0]
1820 self._pcache[id] = nl[0]
1811 return nl[0]
1821 return nl[0]
1812 raise AmbiguousPrefixLookupError(id, self.indexfile,
1822 raise AmbiguousPrefixLookupError(id, self.indexfile,
1813 _('ambiguous identifier'))
1823 _('ambiguous identifier'))
1814 if maybewdir:
1824 if maybewdir:
1815 raise error.WdirUnsupported
1825 raise error.WdirUnsupported
1816 return None
1826 return None
1817 except TypeError:
1827 except TypeError:
1818 pass
1828 pass
1819
1829
1820 def lookup(self, id):
1830 def lookup(self, id):
1821 """locate a node based on:
1831 """locate a node based on:
1822 - revision number or str(revision number)
1832 - revision number or str(revision number)
1823 - nodeid or subset of hex nodeid
1833 - nodeid or subset of hex nodeid
1824 """
1834 """
1825 n = self._match(id)
1835 n = self._match(id)
1826 if n is not None:
1836 if n is not None:
1827 return n
1837 return n
1828 n = self._partialmatch(id)
1838 n = self._partialmatch(id)
1829 if n:
1839 if n:
1830 return n
1840 return n
1831
1841
1832 raise LookupError(id, self.indexfile, _('no match found'))
1842 raise LookupError(id, self.indexfile, _('no match found'))
1833
1843
1834 def shortest(self, node, minlength=1):
1844 def shortest(self, node, minlength=1):
1835 """Find the shortest unambiguous prefix that matches node."""
1845 """Find the shortest unambiguous prefix that matches node."""
1836 def isvalid(prefix):
1846 def isvalid(prefix):
1837 try:
1847 try:
1838 node = self._partialmatch(prefix)
1848 node = self._partialmatch(prefix)
1839 except error.RevlogError:
1849 except error.RevlogError:
1840 return False
1850 return False
1841 except error.WdirUnsupported:
1851 except error.WdirUnsupported:
1842 # single 'ff...' match
1852 # single 'ff...' match
1843 return True
1853 return True
1844 if node is None:
1854 if node is None:
1845 raise LookupError(node, self.indexfile, _('no node'))
1855 raise LookupError(node, self.indexfile, _('no node'))
1846 return True
1856 return True
1847
1857
1848 def maybewdir(prefix):
1858 def maybewdir(prefix):
1849 return all(c == 'f' for c in prefix)
1859 return all(c == 'f' for c in prefix)
1850
1860
1851 hexnode = hex(node)
1861 hexnode = hex(node)
1852
1862
1853 def disambiguate(hexnode, minlength):
1863 def disambiguate(hexnode, minlength):
1854 """Disambiguate against wdirid."""
1864 """Disambiguate against wdirid."""
1855 for length in range(minlength, 41):
1865 for length in range(minlength, 41):
1856 prefix = hexnode[:length]
1866 prefix = hexnode[:length]
1857 if not maybewdir(prefix):
1867 if not maybewdir(prefix):
1858 return prefix
1868 return prefix
1859
1869
1860 if not getattr(self, 'filteredrevs', None):
1870 if not getattr(self, 'filteredrevs', None):
1861 try:
1871 try:
1862 length = max(self.index.shortest(node), minlength)
1872 length = max(self.index.shortest(node), minlength)
1863 return disambiguate(hexnode, length)
1873 return disambiguate(hexnode, length)
1864 except RevlogError:
1874 except RevlogError:
1865 if node != wdirid:
1875 if node != wdirid:
1866 raise LookupError(node, self.indexfile, _('no node'))
1876 raise LookupError(node, self.indexfile, _('no node'))
1867 except AttributeError:
1877 except AttributeError:
1868 # Fall through to pure code
1878 # Fall through to pure code
1869 pass
1879 pass
1870
1880
1871 if node == wdirid:
1881 if node == wdirid:
1872 for length in range(minlength, 41):
1882 for length in range(minlength, 41):
1873 prefix = hexnode[:length]
1883 prefix = hexnode[:length]
1874 if isvalid(prefix):
1884 if isvalid(prefix):
1875 return prefix
1885 return prefix
1876
1886
1877 for length in range(minlength, 41):
1887 for length in range(minlength, 41):
1878 prefix = hexnode[:length]
1888 prefix = hexnode[:length]
1879 if isvalid(prefix):
1889 if isvalid(prefix):
1880 return disambiguate(hexnode, length)
1890 return disambiguate(hexnode, length)
1881
1891
1882 def cmp(self, node, text):
1892 def cmp(self, node, text):
1883 """compare text with a given file revision
1893 """compare text with a given file revision
1884
1894
1885 returns True if text is different than what is stored.
1895 returns True if text is different than what is stored.
1886 """
1896 """
1887 p1, p2 = self.parents(node)
1897 p1, p2 = self.parents(node)
1888 return hash(text, p1, p2) != node
1898 return hash(text, p1, p2) != node
1889
1899
1890 def _cachesegment(self, offset, data):
1900 def _cachesegment(self, offset, data):
1891 """Add a segment to the revlog cache.
1901 """Add a segment to the revlog cache.
1892
1902
1893 Accepts an absolute offset and the data that is at that location.
1903 Accepts an absolute offset and the data that is at that location.
1894 """
1904 """
1895 o, d = self._chunkcache
1905 o, d = self._chunkcache
1896 # try to add to existing cache
1906 # try to add to existing cache
1897 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1907 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1898 self._chunkcache = o, d + data
1908 self._chunkcache = o, d + data
1899 else:
1909 else:
1900 self._chunkcache = offset, data
1910 self._chunkcache = offset, data
1901
1911
1902 def _readsegment(self, offset, length, df=None):
1912 def _readsegment(self, offset, length, df=None):
1903 """Load a segment of raw data from the revlog.
1913 """Load a segment of raw data from the revlog.
1904
1914
1905 Accepts an absolute offset, length to read, and an optional existing
1915 Accepts an absolute offset, length to read, and an optional existing
1906 file handle to read from.
1916 file handle to read from.
1907
1917
1908 If an existing file handle is passed, it will be seeked and the
1918 If an existing file handle is passed, it will be seeked and the
1909 original seek position will NOT be restored.
1919 original seek position will NOT be restored.
1910
1920
1911 Returns a str or buffer of raw byte data.
1921 Returns a str or buffer of raw byte data.
1912 """
1922 """
1913 # Cache data both forward and backward around the requested
1923 # Cache data both forward and backward around the requested
1914 # data, in a fixed size window. This helps speed up operations
1924 # data, in a fixed size window. This helps speed up operations
1915 # involving reading the revlog backwards.
1925 # involving reading the revlog backwards.
1916 cachesize = self._chunkcachesize
1926 cachesize = self._chunkcachesize
1917 realoffset = offset & ~(cachesize - 1)
1927 realoffset = offset & ~(cachesize - 1)
1918 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1928 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1919 - realoffset)
1929 - realoffset)
1920 with self._datareadfp(df) as df:
1930 with self._datareadfp(df) as df:
1921 df.seek(realoffset)
1931 df.seek(realoffset)
1922 d = df.read(reallength)
1932 d = df.read(reallength)
1923 self._cachesegment(realoffset, d)
1933 self._cachesegment(realoffset, d)
1924 if offset != realoffset or reallength != length:
1934 if offset != realoffset or reallength != length:
1925 return util.buffer(d, offset - realoffset, length)
1935 return util.buffer(d, offset - realoffset, length)
1926 return d
1936 return d
1927
1937
1928 def _getsegment(self, offset, length, df=None):
1938 def _getsegment(self, offset, length, df=None):
1929 """Obtain a segment of raw data from the revlog.
1939 """Obtain a segment of raw data from the revlog.
1930
1940
1931 Accepts an absolute offset, length of bytes to obtain, and an
1941 Accepts an absolute offset, length of bytes to obtain, and an
1932 optional file handle to the already-opened revlog. If the file
1942 optional file handle to the already-opened revlog. If the file
1933 handle is used, it's original seek position will not be preserved.
1943 handle is used, it's original seek position will not be preserved.
1934
1944
1935 Requests for data may be returned from a cache.
1945 Requests for data may be returned from a cache.
1936
1946
1937 Returns a str or a buffer instance of raw byte data.
1947 Returns a str or a buffer instance of raw byte data.
1938 """
1948 """
1939 o, d = self._chunkcache
1949 o, d = self._chunkcache
1940 l = len(d)
1950 l = len(d)
1941
1951
1942 # is it in the cache?
1952 # is it in the cache?
1943 cachestart = offset - o
1953 cachestart = offset - o
1944 cacheend = cachestart + length
1954 cacheend = cachestart + length
1945 if cachestart >= 0 and cacheend <= l:
1955 if cachestart >= 0 and cacheend <= l:
1946 if cachestart == 0 and cacheend == l:
1956 if cachestart == 0 and cacheend == l:
1947 return d # avoid a copy
1957 return d # avoid a copy
1948 return util.buffer(d, cachestart, cacheend - cachestart)
1958 return util.buffer(d, cachestart, cacheend - cachestart)
1949
1959
1950 return self._readsegment(offset, length, df=df)
1960 return self._readsegment(offset, length, df=df)
1951
1961
1952 def _getsegmentforrevs(self, startrev, endrev, df=None):
1962 def _getsegmentforrevs(self, startrev, endrev, df=None):
1953 """Obtain a segment of raw data corresponding to a range of revisions.
1963 """Obtain a segment of raw data corresponding to a range of revisions.
1954
1964
1955 Accepts the start and end revisions and an optional already-open
1965 Accepts the start and end revisions and an optional already-open
1956 file handle to be used for reading. If the file handle is read, its
1966 file handle to be used for reading. If the file handle is read, its
1957 seek position will not be preserved.
1967 seek position will not be preserved.
1958
1968
1959 Requests for data may be satisfied by a cache.
1969 Requests for data may be satisfied by a cache.
1960
1970
1961 Returns a 2-tuple of (offset, data) for the requested range of
1971 Returns a 2-tuple of (offset, data) for the requested range of
1962 revisions. Offset is the integer offset from the beginning of the
1972 revisions. Offset is the integer offset from the beginning of the
1963 revlog and data is a str or buffer of the raw byte data.
1973 revlog and data is a str or buffer of the raw byte data.
1964
1974
1965 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1975 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1966 to determine where each revision's data begins and ends.
1976 to determine where each revision's data begins and ends.
1967 """
1977 """
1968 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1978 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1969 # (functions are expensive).
1979 # (functions are expensive).
1970 index = self.index
1980 index = self.index
1971 istart = index[startrev]
1981 istart = index[startrev]
1972 start = int(istart[0] >> 16)
1982 start = int(istart[0] >> 16)
1973 if startrev == endrev:
1983 if startrev == endrev:
1974 end = start + istart[1]
1984 end = start + istart[1]
1975 else:
1985 else:
1976 iend = index[endrev]
1986 iend = index[endrev]
1977 end = int(iend[0] >> 16) + iend[1]
1987 end = int(iend[0] >> 16) + iend[1]
1978
1988
1979 if self._inline:
1989 if self._inline:
1980 start += (startrev + 1) * self._io.size
1990 start += (startrev + 1) * self._io.size
1981 end += (endrev + 1) * self._io.size
1991 end += (endrev + 1) * self._io.size
1982 length = end - start
1992 length = end - start
1983
1993
1984 return start, self._getsegment(start, length, df=df)
1994 return start, self._getsegment(start, length, df=df)
1985
1995
1986 def _chunk(self, rev, df=None):
1996 def _chunk(self, rev, df=None):
1987 """Obtain a single decompressed chunk for a revision.
1997 """Obtain a single decompressed chunk for a revision.
1988
1998
1989 Accepts an integer revision and an optional already-open file handle
1999 Accepts an integer revision and an optional already-open file handle
1990 to be used for reading. If used, the seek position of the file will not
2000 to be used for reading. If used, the seek position of the file will not
1991 be preserved.
2001 be preserved.
1992
2002
1993 Returns a str holding uncompressed data for the requested revision.
2003 Returns a str holding uncompressed data for the requested revision.
1994 """
2004 """
1995 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
2005 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1996
2006
1997 def _chunks(self, revs, df=None, targetsize=None):
2007 def _chunks(self, revs, df=None, targetsize=None):
1998 """Obtain decompressed chunks for the specified revisions.
2008 """Obtain decompressed chunks for the specified revisions.
1999
2009
2000 Accepts an iterable of numeric revisions that are assumed to be in
2010 Accepts an iterable of numeric revisions that are assumed to be in
2001 ascending order. Also accepts an optional already-open file handle
2011 ascending order. Also accepts an optional already-open file handle
2002 to be used for reading. If used, the seek position of the file will
2012 to be used for reading. If used, the seek position of the file will
2003 not be preserved.
2013 not be preserved.
2004
2014
2005 This function is similar to calling ``self._chunk()`` multiple times,
2015 This function is similar to calling ``self._chunk()`` multiple times,
2006 but is faster.
2016 but is faster.
2007
2017
2008 Returns a list with decompressed data for each requested revision.
2018 Returns a list with decompressed data for each requested revision.
2009 """
2019 """
2010 if not revs:
2020 if not revs:
2011 return []
2021 return []
2012 start = self.start
2022 start = self.start
2013 length = self.length
2023 length = self.length
2014 inline = self._inline
2024 inline = self._inline
2015 iosize = self._io.size
2025 iosize = self._io.size
2016 buffer = util.buffer
2026 buffer = util.buffer
2017
2027
2018 l = []
2028 l = []
2019 ladd = l.append
2029 ladd = l.append
2020
2030
2021 if not self._withsparseread:
2031 if not self._withsparseread:
2022 slicedchunks = (revs,)
2032 slicedchunks = (revs,)
2023 else:
2033 else:
2024 slicedchunks = _slicechunk(self, revs, targetsize=targetsize)
2034 slicedchunks = _slicechunk(self, revs, targetsize=targetsize)
2025
2035
2026 for revschunk in slicedchunks:
2036 for revschunk in slicedchunks:
2027 firstrev = revschunk[0]
2037 firstrev = revschunk[0]
2028 # Skip trailing revisions with empty diff
2038 # Skip trailing revisions with empty diff
2029 for lastrev in revschunk[::-1]:
2039 for lastrev in revschunk[::-1]:
2030 if length(lastrev) != 0:
2040 if length(lastrev) != 0:
2031 break
2041 break
2032
2042
2033 try:
2043 try:
2034 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
2044 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
2035 except OverflowError:
2045 except OverflowError:
2036 # issue4215 - we can't cache a run of chunks greater than
2046 # issue4215 - we can't cache a run of chunks greater than
2037 # 2G on Windows
2047 # 2G on Windows
2038 return [self._chunk(rev, df=df) for rev in revschunk]
2048 return [self._chunk(rev, df=df) for rev in revschunk]
2039
2049
2040 decomp = self.decompress
2050 decomp = self.decompress
2041 for rev in revschunk:
2051 for rev in revschunk:
2042 chunkstart = start(rev)
2052 chunkstart = start(rev)
2043 if inline:
2053 if inline:
2044 chunkstart += (rev + 1) * iosize
2054 chunkstart += (rev + 1) * iosize
2045 chunklength = length(rev)
2055 chunklength = length(rev)
2046 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
2056 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
2047
2057
2048 return l
2058 return l
2049
2059
2050 def _chunkclear(self):
2060 def _chunkclear(self):
2051 """Clear the raw chunk cache."""
2061 """Clear the raw chunk cache."""
2052 self._chunkcache = (0, '')
2062 self._chunkcache = (0, '')
2053
2063
2054 def deltaparent(self, rev):
2064 def deltaparent(self, rev):
2055 """return deltaparent of the given revision"""
2065 """return deltaparent of the given revision"""
2056 base = self.index[rev][3]
2066 base = self.index[rev][3]
2057 if base == rev:
2067 if base == rev:
2058 return nullrev
2068 return nullrev
2059 elif self._generaldelta:
2069 elif self._generaldelta:
2060 return base
2070 return base
2061 else:
2071 else:
2062 return rev - 1
2072 return rev - 1
2063
2073
2064 def revdiff(self, rev1, rev2):
2074 def revdiff(self, rev1, rev2):
2065 """return or calculate a delta between two revisions
2075 """return or calculate a delta between two revisions
2066
2076
2067 The delta calculated is in binary form and is intended to be written to
2077 The delta calculated is in binary form and is intended to be written to
2068 revlog data directly. So this function needs raw revision data.
2078 revlog data directly. So this function needs raw revision data.
2069 """
2079 """
2070 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
2080 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
2071 return bytes(self._chunk(rev2))
2081 return bytes(self._chunk(rev2))
2072
2082
2073 return mdiff.textdiff(self.revision(rev1, raw=True),
2083 return mdiff.textdiff(self.revision(rev1, raw=True),
2074 self.revision(rev2, raw=True))
2084 self.revision(rev2, raw=True))
2075
2085
2076 def revision(self, nodeorrev, _df=None, raw=False):
2086 def revision(self, nodeorrev, _df=None, raw=False):
2077 """return an uncompressed revision of a given node or revision
2087 """return an uncompressed revision of a given node or revision
2078 number.
2088 number.
2079
2089
2080 _df - an existing file handle to read from. (internal-only)
2090 _df - an existing file handle to read from. (internal-only)
2081 raw - an optional argument specifying if the revision data is to be
2091 raw - an optional argument specifying if the revision data is to be
2082 treated as raw data when applying flag transforms. 'raw' should be set
2092 treated as raw data when applying flag transforms. 'raw' should be set
2083 to True when generating changegroups or in debug commands.
2093 to True when generating changegroups or in debug commands.
2084 """
2094 """
2085 if isinstance(nodeorrev, int):
2095 if isinstance(nodeorrev, int):
2086 rev = nodeorrev
2096 rev = nodeorrev
2087 node = self.node(rev)
2097 node = self.node(rev)
2088 else:
2098 else:
2089 node = nodeorrev
2099 node = nodeorrev
2090 rev = None
2100 rev = None
2091
2101
2092 cachedrev = None
2102 cachedrev = None
2093 flags = None
2103 flags = None
2094 rawtext = None
2104 rawtext = None
2095 if node == nullid:
2105 if node == nullid:
2096 return ""
2106 return ""
2097 if self._cache:
2107 if self._cache:
2098 if self._cache[0] == node:
2108 if self._cache[0] == node:
2099 # _cache only stores rawtext
2109 # _cache only stores rawtext
2100 if raw:
2110 if raw:
2101 return self._cache[2]
2111 return self._cache[2]
2102 # duplicated, but good for perf
2112 # duplicated, but good for perf
2103 if rev is None:
2113 if rev is None:
2104 rev = self.rev(node)
2114 rev = self.rev(node)
2105 if flags is None:
2115 if flags is None:
2106 flags = self.flags(rev)
2116 flags = self.flags(rev)
2107 # no extra flags set, no flag processor runs, text = rawtext
2117 # no extra flags set, no flag processor runs, text = rawtext
2108 if flags == REVIDX_DEFAULT_FLAGS:
2118 if flags == REVIDX_DEFAULT_FLAGS:
2109 return self._cache[2]
2119 return self._cache[2]
2110 # rawtext is reusable. need to run flag processor
2120 # rawtext is reusable. need to run flag processor
2111 rawtext = self._cache[2]
2121 rawtext = self._cache[2]
2112
2122
2113 cachedrev = self._cache[1]
2123 cachedrev = self._cache[1]
2114
2124
2115 # look up what we need to read
2125 # look up what we need to read
2116 if rawtext is None:
2126 if rawtext is None:
2117 if rev is None:
2127 if rev is None:
2118 rev = self.rev(node)
2128 rev = self.rev(node)
2119
2129
2120 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
2130 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
2121 if stopped:
2131 if stopped:
2122 rawtext = self._cache[2]
2132 rawtext = self._cache[2]
2123
2133
2124 # drop cache to save memory
2134 # drop cache to save memory
2125 self._cache = None
2135 self._cache = None
2126
2136
2127 targetsize = None
2137 targetsize = None
2128 rawsize = self.index[rev][2]
2138 rawsize = self.index[rev][2]
2129 if 0 <= rawsize:
2139 if 0 <= rawsize:
2130 targetsize = 4 * rawsize
2140 targetsize = 4 * rawsize
2131
2141
2132 bins = self._chunks(chain, df=_df, targetsize=targetsize)
2142 bins = self._chunks(chain, df=_df, targetsize=targetsize)
2133 if rawtext is None:
2143 if rawtext is None:
2134 rawtext = bytes(bins[0])
2144 rawtext = bytes(bins[0])
2135 bins = bins[1:]
2145 bins = bins[1:]
2136
2146
2137 rawtext = mdiff.patches(rawtext, bins)
2147 rawtext = mdiff.patches(rawtext, bins)
2138 self._cache = (node, rev, rawtext)
2148 self._cache = (node, rev, rawtext)
2139
2149
2140 if flags is None:
2150 if flags is None:
2141 if rev is None:
2151 if rev is None:
2142 rev = self.rev(node)
2152 rev = self.rev(node)
2143 flags = self.flags(rev)
2153 flags = self.flags(rev)
2144
2154
2145 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
2155 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
2146 if validatehash:
2156 if validatehash:
2147 self.checkhash(text, node, rev=rev)
2157 self.checkhash(text, node, rev=rev)
2148
2158
2149 return text
2159 return text
2150
2160
2151 def hash(self, text, p1, p2):
2161 def hash(self, text, p1, p2):
2152 """Compute a node hash.
2162 """Compute a node hash.
2153
2163
2154 Available as a function so that subclasses can replace the hash
2164 Available as a function so that subclasses can replace the hash
2155 as needed.
2165 as needed.
2156 """
2166 """
2157 return hash(text, p1, p2)
2167 return hash(text, p1, p2)
2158
2168
2159 def _processflags(self, text, flags, operation, raw=False):
2169 def _processflags(self, text, flags, operation, raw=False):
2160 """Inspect revision data flags and applies transforms defined by
2170 """Inspect revision data flags and applies transforms defined by
2161 registered flag processors.
2171 registered flag processors.
2162
2172
2163 ``text`` - the revision data to process
2173 ``text`` - the revision data to process
2164 ``flags`` - the revision flags
2174 ``flags`` - the revision flags
2165 ``operation`` - the operation being performed (read or write)
2175 ``operation`` - the operation being performed (read or write)
2166 ``raw`` - an optional argument describing if the raw transform should be
2176 ``raw`` - an optional argument describing if the raw transform should be
2167 applied.
2177 applied.
2168
2178
2169 This method processes the flags in the order (or reverse order if
2179 This method processes the flags in the order (or reverse order if
2170 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
2180 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
2171 flag processors registered for present flags. The order of flags defined
2181 flag processors registered for present flags. The order of flags defined
2172 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
2182 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
2173
2183
2174 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
2184 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
2175 processed text and ``validatehash`` is a bool indicating whether the
2185 processed text and ``validatehash`` is a bool indicating whether the
2176 returned text should be checked for hash integrity.
2186 returned text should be checked for hash integrity.
2177
2187
2178 Note: If the ``raw`` argument is set, it has precedence over the
2188 Note: If the ``raw`` argument is set, it has precedence over the
2179 operation and will only update the value of ``validatehash``.
2189 operation and will only update the value of ``validatehash``.
2180 """
2190 """
2181 # fast path: no flag processors will run
2191 # fast path: no flag processors will run
2182 if flags == 0:
2192 if flags == 0:
2183 return text, True
2193 return text, True
2184 if not operation in ('read', 'write'):
2194 if not operation in ('read', 'write'):
2185 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
2195 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
2186 # Check all flags are known.
2196 # Check all flags are known.
2187 if flags & ~REVIDX_KNOWN_FLAGS:
2197 if flags & ~REVIDX_KNOWN_FLAGS:
2188 raise RevlogError(_("incompatible revision flag '%#x'") %
2198 raise RevlogError(_("incompatible revision flag '%#x'") %
2189 (flags & ~REVIDX_KNOWN_FLAGS))
2199 (flags & ~REVIDX_KNOWN_FLAGS))
2190 validatehash = True
2200 validatehash = True
2191 # Depending on the operation (read or write), the order might be
2201 # Depending on the operation (read or write), the order might be
2192 # reversed due to non-commutative transforms.
2202 # reversed due to non-commutative transforms.
2193 orderedflags = REVIDX_FLAGS_ORDER
2203 orderedflags = REVIDX_FLAGS_ORDER
2194 if operation == 'write':
2204 if operation == 'write':
2195 orderedflags = reversed(orderedflags)
2205 orderedflags = reversed(orderedflags)
2196
2206
2197 for flag in orderedflags:
2207 for flag in orderedflags:
2198 # If a flagprocessor has been registered for a known flag, apply the
2208 # If a flagprocessor has been registered for a known flag, apply the
2199 # related operation transform and update result tuple.
2209 # related operation transform and update result tuple.
2200 if flag & flags:
2210 if flag & flags:
2201 vhash = True
2211 vhash = True
2202
2212
2203 if flag not in _flagprocessors:
2213 if flag not in _flagprocessors:
2204 message = _("missing processor for flag '%#x'") % (flag)
2214 message = _("missing processor for flag '%#x'") % (flag)
2205 raise RevlogError(message)
2215 raise RevlogError(message)
2206
2216
2207 processor = _flagprocessors[flag]
2217 processor = _flagprocessors[flag]
2208 if processor is not None:
2218 if processor is not None:
2209 readtransform, writetransform, rawtransform = processor
2219 readtransform, writetransform, rawtransform = processor
2210
2220
2211 if raw:
2221 if raw:
2212 vhash = rawtransform(self, text)
2222 vhash = rawtransform(self, text)
2213 elif operation == 'read':
2223 elif operation == 'read':
2214 text, vhash = readtransform(self, text)
2224 text, vhash = readtransform(self, text)
2215 else: # write operation
2225 else: # write operation
2216 text, vhash = writetransform(self, text)
2226 text, vhash = writetransform(self, text)
2217 validatehash = validatehash and vhash
2227 validatehash = validatehash and vhash
2218
2228
2219 return text, validatehash
2229 return text, validatehash
2220
2230
2221 def checkhash(self, text, node, p1=None, p2=None, rev=None):
2231 def checkhash(self, text, node, p1=None, p2=None, rev=None):
2222 """Check node hash integrity.
2232 """Check node hash integrity.
2223
2233
2224 Available as a function so that subclasses can extend hash mismatch
2234 Available as a function so that subclasses can extend hash mismatch
2225 behaviors as needed.
2235 behaviors as needed.
2226 """
2236 """
2227 try:
2237 try:
2228 if p1 is None and p2 is None:
2238 if p1 is None and p2 is None:
2229 p1, p2 = self.parents(node)
2239 p1, p2 = self.parents(node)
2230 if node != self.hash(text, p1, p2):
2240 if node != self.hash(text, p1, p2):
2231 revornode = rev
2241 revornode = rev
2232 if revornode is None:
2242 if revornode is None:
2233 revornode = templatefilters.short(hex(node))
2243 revornode = templatefilters.short(hex(node))
2234 raise RevlogError(_("integrity check failed on %s:%s")
2244 raise RevlogError(_("integrity check failed on %s:%s")
2235 % (self.indexfile, pycompat.bytestr(revornode)))
2245 % (self.indexfile, pycompat.bytestr(revornode)))
2236 except RevlogError:
2246 except RevlogError:
2237 if self._censorable and _censoredtext(text):
2247 if self._censorable and _censoredtext(text):
2238 raise error.CensoredNodeError(self.indexfile, node, text)
2248 raise error.CensoredNodeError(self.indexfile, node, text)
2239 raise
2249 raise
2240
2250
2241 def _enforceinlinesize(self, tr, fp=None):
2251 def _enforceinlinesize(self, tr, fp=None):
2242 """Check if the revlog is too big for inline and convert if so.
2252 """Check if the revlog is too big for inline and convert if so.
2243
2253
2244 This should be called after revisions are added to the revlog. If the
2254 This should be called after revisions are added to the revlog. If the
2245 revlog has grown too large to be an inline revlog, it will convert it
2255 revlog has grown too large to be an inline revlog, it will convert it
2246 to use multiple index and data files.
2256 to use multiple index and data files.
2247 """
2257 """
2248 tiprev = len(self) - 1
2258 tiprev = len(self) - 1
2249 if (not self._inline or
2259 if (not self._inline or
2250 (self.start(tiprev) + self.length(tiprev)) < _maxinline):
2260 (self.start(tiprev) + self.length(tiprev)) < _maxinline):
2251 return
2261 return
2252
2262
2253 trinfo = tr.find(self.indexfile)
2263 trinfo = tr.find(self.indexfile)
2254 if trinfo is None:
2264 if trinfo is None:
2255 raise RevlogError(_("%s not found in the transaction")
2265 raise RevlogError(_("%s not found in the transaction")
2256 % self.indexfile)
2266 % self.indexfile)
2257
2267
2258 trindex = trinfo[2]
2268 trindex = trinfo[2]
2259 if trindex is not None:
2269 if trindex is not None:
2260 dataoff = self.start(trindex)
2270 dataoff = self.start(trindex)
2261 else:
2271 else:
2262 # revlog was stripped at start of transaction, use all leftover data
2272 # revlog was stripped at start of transaction, use all leftover data
2263 trindex = len(self) - 1
2273 trindex = len(self) - 1
2264 dataoff = self.end(tiprev)
2274 dataoff = self.end(tiprev)
2265
2275
2266 tr.add(self.datafile, dataoff)
2276 tr.add(self.datafile, dataoff)
2267
2277
2268 if fp:
2278 if fp:
2269 fp.flush()
2279 fp.flush()
2270 fp.close()
2280 fp.close()
2271
2281
2272 with self._datafp('w') as df:
2282 with self._datafp('w') as df:
2273 for r in self:
2283 for r in self:
2274 df.write(self._getsegmentforrevs(r, r)[1])
2284 df.write(self._getsegmentforrevs(r, r)[1])
2275
2285
2276 with self._indexfp('w') as fp:
2286 with self._indexfp('w') as fp:
2277 self.version &= ~FLAG_INLINE_DATA
2287 self.version &= ~FLAG_INLINE_DATA
2278 self._inline = False
2288 self._inline = False
2279 io = self._io
2289 io = self._io
2280 for i in self:
2290 for i in self:
2281 e = io.packentry(self.index[i], self.node, self.version, i)
2291 e = io.packentry(self.index[i], self.node, self.version, i)
2282 fp.write(e)
2292 fp.write(e)
2283
2293
2284 # the temp file replace the real index when we exit the context
2294 # the temp file replace the real index when we exit the context
2285 # manager
2295 # manager
2286
2296
2287 tr.replace(self.indexfile, trindex * self._io.size)
2297 tr.replace(self.indexfile, trindex * self._io.size)
2288 self._chunkclear()
2298 self._chunkclear()
2289
2299
2290 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
2300 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
2291 node=None, flags=REVIDX_DEFAULT_FLAGS, deltacomputer=None):
2301 node=None, flags=REVIDX_DEFAULT_FLAGS, deltacomputer=None):
2292 """add a revision to the log
2302 """add a revision to the log
2293
2303
2294 text - the revision data to add
2304 text - the revision data to add
2295 transaction - the transaction object used for rollback
2305 transaction - the transaction object used for rollback
2296 link - the linkrev data to add
2306 link - the linkrev data to add
2297 p1, p2 - the parent nodeids of the revision
2307 p1, p2 - the parent nodeids of the revision
2298 cachedelta - an optional precomputed delta
2308 cachedelta - an optional precomputed delta
2299 node - nodeid of revision; typically node is not specified, and it is
2309 node - nodeid of revision; typically node is not specified, and it is
2300 computed by default as hash(text, p1, p2), however subclasses might
2310 computed by default as hash(text, p1, p2), however subclasses might
2301 use different hashing method (and override checkhash() in such case)
2311 use different hashing method (and override checkhash() in such case)
2302 flags - the known flags to set on the revision
2312 flags - the known flags to set on the revision
2303 deltacomputer - an optional _deltacomputer instance shared between
2313 deltacomputer - an optional _deltacomputer instance shared between
2304 multiple calls
2314 multiple calls
2305 """
2315 """
2306 if link == nullrev:
2316 if link == nullrev:
2307 raise RevlogError(_("attempted to add linkrev -1 to %s")
2317 raise RevlogError(_("attempted to add linkrev -1 to %s")
2308 % self.indexfile)
2318 % self.indexfile)
2309
2319
2310 if flags:
2320 if flags:
2311 node = node or self.hash(text, p1, p2)
2321 node = node or self.hash(text, p1, p2)
2312
2322
2313 rawtext, validatehash = self._processflags(text, flags, 'write')
2323 rawtext, validatehash = self._processflags(text, flags, 'write')
2314
2324
2315 # If the flag processor modifies the revision data, ignore any provided
2325 # If the flag processor modifies the revision data, ignore any provided
2316 # cachedelta.
2326 # cachedelta.
2317 if rawtext != text:
2327 if rawtext != text:
2318 cachedelta = None
2328 cachedelta = None
2319
2329
2320 if len(rawtext) > _maxentrysize:
2330 if len(rawtext) > _maxentrysize:
2321 raise RevlogError(
2331 raise RevlogError(
2322 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
2332 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
2323 % (self.indexfile, len(rawtext)))
2333 % (self.indexfile, len(rawtext)))
2324
2334
2325 node = node or self.hash(rawtext, p1, p2)
2335 node = node or self.hash(rawtext, p1, p2)
2326 if node in self.nodemap:
2336 if node in self.nodemap:
2327 return node
2337 return node
2328
2338
2329 if validatehash:
2339 if validatehash:
2330 self.checkhash(rawtext, node, p1=p1, p2=p2)
2340 self.checkhash(rawtext, node, p1=p1, p2=p2)
2331
2341
2332 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
2342 return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
2333 flags, cachedelta=cachedelta,
2343 flags, cachedelta=cachedelta,
2334 deltacomputer=deltacomputer)
2344 deltacomputer=deltacomputer)
2335
2345
2336 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
2346 def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
2337 cachedelta=None, deltacomputer=None):
2347 cachedelta=None, deltacomputer=None):
2338 """add a raw revision with known flags, node and parents
2348 """add a raw revision with known flags, node and parents
2339 useful when reusing a revision not stored in this revlog (ex: received
2349 useful when reusing a revision not stored in this revlog (ex: received
2340 over wire, or read from an external bundle).
2350 over wire, or read from an external bundle).
2341 """
2351 """
2342 dfh = None
2352 dfh = None
2343 if not self._inline:
2353 if not self._inline:
2344 dfh = self._datafp("a+")
2354 dfh = self._datafp("a+")
2345 ifh = self._indexfp("a+")
2355 ifh = self._indexfp("a+")
2346 try:
2356 try:
2347 return self._addrevision(node, rawtext, transaction, link, p1, p2,
2357 return self._addrevision(node, rawtext, transaction, link, p1, p2,
2348 flags, cachedelta, ifh, dfh,
2358 flags, cachedelta, ifh, dfh,
2349 deltacomputer=deltacomputer)
2359 deltacomputer=deltacomputer)
2350 finally:
2360 finally:
2351 if dfh:
2361 if dfh:
2352 dfh.close()
2362 dfh.close()
2353 ifh.close()
2363 ifh.close()
2354
2364
2355 def compress(self, data):
2365 def compress(self, data):
2356 """Generate a possibly-compressed representation of data."""
2366 """Generate a possibly-compressed representation of data."""
2357 if not data:
2367 if not data:
2358 return '', data
2368 return '', data
2359
2369
2360 compressed = self._compressor.compress(data)
2370 compressed = self._compressor.compress(data)
2361
2371
2362 if compressed:
2372 if compressed:
2363 # The revlog compressor added the header in the returned data.
2373 # The revlog compressor added the header in the returned data.
2364 return '', compressed
2374 return '', compressed
2365
2375
2366 if data[0:1] == '\0':
2376 if data[0:1] == '\0':
2367 return '', data
2377 return '', data
2368 return 'u', data
2378 return 'u', data
2369
2379
2370 def decompress(self, data):
2380 def decompress(self, data):
2371 """Decompress a revlog chunk.
2381 """Decompress a revlog chunk.
2372
2382
2373 The chunk is expected to begin with a header identifying the
2383 The chunk is expected to begin with a header identifying the
2374 format type so it can be routed to an appropriate decompressor.
2384 format type so it can be routed to an appropriate decompressor.
2375 """
2385 """
2376 if not data:
2386 if not data:
2377 return data
2387 return data
2378
2388
2379 # Revlogs are read much more frequently than they are written and many
2389 # Revlogs are read much more frequently than they are written and many
2380 # chunks only take microseconds to decompress, so performance is
2390 # chunks only take microseconds to decompress, so performance is
2381 # important here.
2391 # important here.
2382 #
2392 #
2383 # We can make a few assumptions about revlogs:
2393 # We can make a few assumptions about revlogs:
2384 #
2394 #
2385 # 1) the majority of chunks will be compressed (as opposed to inline
2395 # 1) the majority of chunks will be compressed (as opposed to inline
2386 # raw data).
2396 # raw data).
2387 # 2) decompressing *any* data will likely by at least 10x slower than
2397 # 2) decompressing *any* data will likely by at least 10x slower than
2388 # returning raw inline data.
2398 # returning raw inline data.
2389 # 3) we want to prioritize common and officially supported compression
2399 # 3) we want to prioritize common and officially supported compression
2390 # engines
2400 # engines
2391 #
2401 #
2392 # It follows that we want to optimize for "decompress compressed data
2402 # It follows that we want to optimize for "decompress compressed data
2393 # when encoded with common and officially supported compression engines"
2403 # when encoded with common and officially supported compression engines"
2394 # case over "raw data" and "data encoded by less common or non-official
2404 # case over "raw data" and "data encoded by less common or non-official
2395 # compression engines." That is why we have the inline lookup first
2405 # compression engines." That is why we have the inline lookup first
2396 # followed by the compengines lookup.
2406 # followed by the compengines lookup.
2397 #
2407 #
2398 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
2408 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
2399 # compressed chunks. And this matters for changelog and manifest reads.
2409 # compressed chunks. And this matters for changelog and manifest reads.
2400 t = data[0:1]
2410 t = data[0:1]
2401
2411
2402 if t == 'x':
2412 if t == 'x':
2403 try:
2413 try:
2404 return _zlibdecompress(data)
2414 return _zlibdecompress(data)
2405 except zlib.error as e:
2415 except zlib.error as e:
2406 raise RevlogError(_('revlog decompress error: %s') %
2416 raise RevlogError(_('revlog decompress error: %s') %
2407 stringutil.forcebytestr(e))
2417 stringutil.forcebytestr(e))
2408 # '\0' is more common than 'u' so it goes first.
2418 # '\0' is more common than 'u' so it goes first.
2409 elif t == '\0':
2419 elif t == '\0':
2410 return data
2420 return data
2411 elif t == 'u':
2421 elif t == 'u':
2412 return util.buffer(data, 1)
2422 return util.buffer(data, 1)
2413
2423
2414 try:
2424 try:
2415 compressor = self._decompressors[t]
2425 compressor = self._decompressors[t]
2416 except KeyError:
2426 except KeyError:
2417 try:
2427 try:
2418 engine = util.compengines.forrevlogheader(t)
2428 engine = util.compengines.forrevlogheader(t)
2419 compressor = engine.revlogcompressor()
2429 compressor = engine.revlogcompressor()
2420 self._decompressors[t] = compressor
2430 self._decompressors[t] = compressor
2421 except KeyError:
2431 except KeyError:
2422 raise RevlogError(_('unknown compression type %r') % t)
2432 raise RevlogError(_('unknown compression type %r') % t)
2423
2433
2424 return compressor.decompress(data)
2434 return compressor.decompress(data)
2425
2435
2426 def _isgooddeltainfo(self, deltainfo, revinfo):
2436 def _isgooddeltainfo(self, deltainfo, revinfo):
2427 """Returns True if the given delta is good. Good means that it is within
2437 """Returns True if the given delta is good. Good means that it is within
2428 the disk span, disk size, and chain length bounds that we know to be
2438 the disk span, disk size, and chain length bounds that we know to be
2429 performant."""
2439 performant."""
2430 if deltainfo is None:
2440 if deltainfo is None:
2431 return False
2441 return False
2432
2442
2433 # - 'deltainfo.distance' is the distance from the base revision --
2443 # - 'deltainfo.distance' is the distance from the base revision --
2434 # bounding it limits the amount of I/O we need to do.
2444 # bounding it limits the amount of I/O we need to do.
2435 # - 'deltainfo.compresseddeltalen' is the sum of the total size of
2445 # - 'deltainfo.compresseddeltalen' is the sum of the total size of
2436 # deltas we need to apply -- bounding it limits the amount of CPU
2446 # deltas we need to apply -- bounding it limits the amount of CPU
2437 # we consume.
2447 # we consume.
2438
2448
2439 if self._sparserevlog:
2449 if self._sparserevlog:
2440 # As sparse-read will be used, we can consider that the distance,
2450 # As sparse-read will be used, we can consider that the distance,
2441 # instead of being the span of the whole chunk,
2451 # instead of being the span of the whole chunk,
2442 # is the span of the largest read chunk
2452 # is the span of the largest read chunk
2443 base = deltainfo.base
2453 base = deltainfo.base
2444
2454
2445 if base != nullrev:
2455 if base != nullrev:
2446 deltachain = self._deltachain(base)[0]
2456 deltachain = self._deltachain(base)[0]
2447 else:
2457 else:
2448 deltachain = []
2458 deltachain = []
2449
2459
2450 chunks = _slicechunk(self, deltachain, deltainfo)
2460 chunks = _slicechunk(self, deltachain, deltainfo)
2451 distance = max(map(lambda revs:_segmentspan(self, revs), chunks))
2461 distance = max(map(lambda revs:_segmentspan(self, revs), chunks))
2452 else:
2462 else:
2453 distance = deltainfo.distance
2463 distance = deltainfo.distance
2454
2464
2455 textlen = revinfo.textlen
2465 textlen = revinfo.textlen
2456 defaultmax = textlen * 4
2466 defaultmax = textlen * 4
2457 maxdist = self._maxdeltachainspan
2467 maxdist = self._maxdeltachainspan
2458 if not maxdist:
2468 if not maxdist:
2459 maxdist = distance # ensure the conditional pass
2469 maxdist = distance # ensure the conditional pass
2460 maxdist = max(maxdist, defaultmax)
2470 maxdist = max(maxdist, defaultmax)
2461 if self._sparserevlog and maxdist < self._srmingapsize:
2471 if self._sparserevlog and maxdist < self._srmingapsize:
2462 # In multiple place, we are ignoring irrelevant data range below a
2472 # In multiple place, we are ignoring irrelevant data range below a
2463 # certain size. Be also apply this tradeoff here and relax span
2473 # certain size. Be also apply this tradeoff here and relax span
2464 # constraint for small enought content.
2474 # constraint for small enought content.
2465 maxdist = self._srmingapsize
2475 maxdist = self._srmingapsize
2466 if (distance > maxdist or deltainfo.deltalen > textlen or
2476 if (distance > maxdist or deltainfo.deltalen > textlen or
2467 deltainfo.compresseddeltalen > textlen * 2 or
2477 deltainfo.compresseddeltalen > textlen * 2 or
2468 (self._maxchainlen and deltainfo.chainlen > self._maxchainlen)):
2478 (self._maxchainlen and deltainfo.chainlen > self._maxchainlen)):
2469 return False
2479 return False
2470
2480
2471 return True
2481 return True
2472
2482
2473 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
2483 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
2474 cachedelta, ifh, dfh, alwayscache=False,
2484 cachedelta, ifh, dfh, alwayscache=False,
2475 deltacomputer=None):
2485 deltacomputer=None):
2476 """internal function to add revisions to the log
2486 """internal function to add revisions to the log
2477
2487
2478 see addrevision for argument descriptions.
2488 see addrevision for argument descriptions.
2479
2489
2480 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2490 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2481
2491
2482 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2492 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2483 be used.
2493 be used.
2484
2494
2485 invariants:
2495 invariants:
2486 - rawtext is optional (can be None); if not set, cachedelta must be set.
2496 - rawtext is optional (can be None); if not set, cachedelta must be set.
2487 if both are set, they must correspond to each other.
2497 if both are set, they must correspond to each other.
2488 """
2498 """
2489 if node == nullid:
2499 if node == nullid:
2490 raise RevlogError(_("%s: attempt to add null revision") %
2500 raise RevlogError(_("%s: attempt to add null revision") %
2491 (self.indexfile))
2501 (self.indexfile))
2492 if node == wdirid or node in wdirfilenodeids:
2502 if node == wdirid or node in wdirfilenodeids:
2493 raise RevlogError(_("%s: attempt to add wdir revision") %
2503 raise RevlogError(_("%s: attempt to add wdir revision") %
2494 (self.indexfile))
2504 (self.indexfile))
2495
2505
2496 if self._inline:
2506 if self._inline:
2497 fh = ifh
2507 fh = ifh
2498 else:
2508 else:
2499 fh = dfh
2509 fh = dfh
2500
2510
2501 btext = [rawtext]
2511 btext = [rawtext]
2502
2512
2503 curr = len(self)
2513 curr = len(self)
2504 prev = curr - 1
2514 prev = curr - 1
2505 offset = self.end(prev)
2515 offset = self.end(prev)
2506 p1r, p2r = self.rev(p1), self.rev(p2)
2516 p1r, p2r = self.rev(p1), self.rev(p2)
2507
2517
2508 # full versions are inserted when the needed deltas
2518 # full versions are inserted when the needed deltas
2509 # become comparable to the uncompressed text
2519 # become comparable to the uncompressed text
2510 if rawtext is None:
2520 if rawtext is None:
2511 # need rawtext size, before changed by flag processors, which is
2521 # need rawtext size, before changed by flag processors, which is
2512 # the non-raw size. use revlog explicitly to avoid filelog's extra
2522 # the non-raw size. use revlog explicitly to avoid filelog's extra
2513 # logic that might remove metadata size.
2523 # logic that might remove metadata size.
2514 textlen = mdiff.patchedsize(revlog.size(self, cachedelta[0]),
2524 textlen = mdiff.patchedsize(revlog.size(self, cachedelta[0]),
2515 cachedelta[1])
2525 cachedelta[1])
2516 else:
2526 else:
2517 textlen = len(rawtext)
2527 textlen = len(rawtext)
2518
2528
2519 if deltacomputer is None:
2529 if deltacomputer is None:
2520 deltacomputer = _deltacomputer(self)
2530 deltacomputer = _deltacomputer(self)
2521
2531
2522 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2532 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2523
2533
2524 # no delta for flag processor revision (see "candelta" for why)
2534 # no delta for flag processor revision (see "candelta" for why)
2525 # not calling candelta since only one revision needs test, also to
2535 # not calling candelta since only one revision needs test, also to
2526 # avoid overhead fetching flags again.
2536 # avoid overhead fetching flags again.
2527 if flags & REVIDX_RAWTEXT_CHANGING_FLAGS:
2537 if flags & REVIDX_RAWTEXT_CHANGING_FLAGS:
2528 deltainfo = None
2538 deltainfo = None
2529 else:
2539 else:
2530 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2540 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2531
2541
2532 if deltainfo is not None:
2542 if deltainfo is not None:
2533 base = deltainfo.base
2543 base = deltainfo.base
2534 chainbase = deltainfo.chainbase
2544 chainbase = deltainfo.chainbase
2535 data = deltainfo.data
2545 data = deltainfo.data
2536 l = deltainfo.deltalen
2546 l = deltainfo.deltalen
2537 else:
2547 else:
2538 rawtext = deltacomputer.buildtext(revinfo, fh)
2548 rawtext = deltacomputer.buildtext(revinfo, fh)
2539 data = self.compress(rawtext)
2549 data = self.compress(rawtext)
2540 l = len(data[1]) + len(data[0])
2550 l = len(data[1]) + len(data[0])
2541 base = chainbase = curr
2551 base = chainbase = curr
2542
2552
2543 e = (offset_type(offset, flags), l, textlen,
2553 e = (offset_type(offset, flags), l, textlen,
2544 base, link, p1r, p2r, node)
2554 base, link, p1r, p2r, node)
2545 self.index.append(e)
2555 self.index.append(e)
2546 self.nodemap[node] = curr
2556 self.nodemap[node] = curr
2547
2557
2548 entry = self._io.packentry(e, self.node, self.version, curr)
2558 entry = self._io.packentry(e, self.node, self.version, curr)
2549 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
2559 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
2550
2560
2551 if alwayscache and rawtext is None:
2561 if alwayscache and rawtext is None:
2552 rawtext = deltacomputer._buildtext(revinfo, fh)
2562 rawtext = deltacomputer._buildtext(revinfo, fh)
2553
2563
2554 if type(rawtext) == bytes: # only accept immutable objects
2564 if type(rawtext) == bytes: # only accept immutable objects
2555 self._cache = (node, curr, rawtext)
2565 self._cache = (node, curr, rawtext)
2556 self._chainbasecache[curr] = chainbase
2566 self._chainbasecache[curr] = chainbase
2557 return node
2567 return node
2558
2568
2559 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2569 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2560 # Files opened in a+ mode have inconsistent behavior on various
2570 # Files opened in a+ mode have inconsistent behavior on various
2561 # platforms. Windows requires that a file positioning call be made
2571 # platforms. Windows requires that a file positioning call be made
2562 # when the file handle transitions between reads and writes. See
2572 # when the file handle transitions between reads and writes. See
2563 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2573 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2564 # platforms, Python or the platform itself can be buggy. Some versions
2574 # platforms, Python or the platform itself can be buggy. Some versions
2565 # of Solaris have been observed to not append at the end of the file
2575 # of Solaris have been observed to not append at the end of the file
2566 # if the file was seeked to before the end. See issue4943 for more.
2576 # if the file was seeked to before the end. See issue4943 for more.
2567 #
2577 #
2568 # We work around this issue by inserting a seek() before writing.
2578 # We work around this issue by inserting a seek() before writing.
2569 # Note: This is likely not necessary on Python 3.
2579 # Note: This is likely not necessary on Python 3.
2570 ifh.seek(0, os.SEEK_END)
2580 ifh.seek(0, os.SEEK_END)
2571 if dfh:
2581 if dfh:
2572 dfh.seek(0, os.SEEK_END)
2582 dfh.seek(0, os.SEEK_END)
2573
2583
2574 curr = len(self) - 1
2584 curr = len(self) - 1
2575 if not self._inline:
2585 if not self._inline:
2576 transaction.add(self.datafile, offset)
2586 transaction.add(self.datafile, offset)
2577 transaction.add(self.indexfile, curr * len(entry))
2587 transaction.add(self.indexfile, curr * len(entry))
2578 if data[0]:
2588 if data[0]:
2579 dfh.write(data[0])
2589 dfh.write(data[0])
2580 dfh.write(data[1])
2590 dfh.write(data[1])
2581 ifh.write(entry)
2591 ifh.write(entry)
2582 else:
2592 else:
2583 offset += curr * self._io.size
2593 offset += curr * self._io.size
2584 transaction.add(self.indexfile, offset, curr)
2594 transaction.add(self.indexfile, offset, curr)
2585 ifh.write(entry)
2595 ifh.write(entry)
2586 ifh.write(data[0])
2596 ifh.write(data[0])
2587 ifh.write(data[1])
2597 ifh.write(data[1])
2588 self._enforceinlinesize(transaction, ifh)
2598 self._enforceinlinesize(transaction, ifh)
2589
2599
2590 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2600 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2591 """
2601 """
2592 add a delta group
2602 add a delta group
2593
2603
2594 given a set of deltas, add them to the revision log. the
2604 given a set of deltas, add them to the revision log. the
2595 first delta is against its parent, which should be in our
2605 first delta is against its parent, which should be in our
2596 log, the rest are against the previous delta.
2606 log, the rest are against the previous delta.
2597
2607
2598 If ``addrevisioncb`` is defined, it will be called with arguments of
2608 If ``addrevisioncb`` is defined, it will be called with arguments of
2599 this revlog and the node that was added.
2609 this revlog and the node that was added.
2600 """
2610 """
2601
2611
2602 nodes = []
2612 nodes = []
2603
2613
2604 r = len(self)
2614 r = len(self)
2605 end = 0
2615 end = 0
2606 if r:
2616 if r:
2607 end = self.end(r - 1)
2617 end = self.end(r - 1)
2608 ifh = self._indexfp("a+")
2618 ifh = self._indexfp("a+")
2609 isize = r * self._io.size
2619 isize = r * self._io.size
2610 if self._inline:
2620 if self._inline:
2611 transaction.add(self.indexfile, end + isize, r)
2621 transaction.add(self.indexfile, end + isize, r)
2612 dfh = None
2622 dfh = None
2613 else:
2623 else:
2614 transaction.add(self.indexfile, isize, r)
2624 transaction.add(self.indexfile, isize, r)
2615 transaction.add(self.datafile, end)
2625 transaction.add(self.datafile, end)
2616 dfh = self._datafp("a+")
2626 dfh = self._datafp("a+")
2617 def flush():
2627 def flush():
2618 if dfh:
2628 if dfh:
2619 dfh.flush()
2629 dfh.flush()
2620 ifh.flush()
2630 ifh.flush()
2621 try:
2631 try:
2622 deltacomputer = _deltacomputer(self)
2632 deltacomputer = _deltacomputer(self)
2623 # loop through our set of deltas
2633 # loop through our set of deltas
2624 for data in deltas:
2634 for data in deltas:
2625 node, p1, p2, linknode, deltabase, delta, flags = data
2635 node, p1, p2, linknode, deltabase, delta, flags = data
2626 link = linkmapper(linknode)
2636 link = linkmapper(linknode)
2627 flags = flags or REVIDX_DEFAULT_FLAGS
2637 flags = flags or REVIDX_DEFAULT_FLAGS
2628
2638
2629 nodes.append(node)
2639 nodes.append(node)
2630
2640
2631 if node in self.nodemap:
2641 if node in self.nodemap:
2632 # this can happen if two branches make the same change
2642 # this can happen if two branches make the same change
2633 continue
2643 continue
2634
2644
2635 for p in (p1, p2):
2645 for p in (p1, p2):
2636 if p not in self.nodemap:
2646 if p not in self.nodemap:
2637 raise LookupError(p, self.indexfile,
2647 raise LookupError(p, self.indexfile,
2638 _('unknown parent'))
2648 _('unknown parent'))
2639
2649
2640 if deltabase not in self.nodemap:
2650 if deltabase not in self.nodemap:
2641 raise LookupError(deltabase, self.indexfile,
2651 raise LookupError(deltabase, self.indexfile,
2642 _('unknown delta base'))
2652 _('unknown delta base'))
2643
2653
2644 baserev = self.rev(deltabase)
2654 baserev = self.rev(deltabase)
2645
2655
2646 if baserev != nullrev and self.iscensored(baserev):
2656 if baserev != nullrev and self.iscensored(baserev):
2647 # if base is censored, delta must be full replacement in a
2657 # if base is censored, delta must be full replacement in a
2648 # single patch operation
2658 # single patch operation
2649 hlen = struct.calcsize(">lll")
2659 hlen = struct.calcsize(">lll")
2650 oldlen = self.rawsize(baserev)
2660 oldlen = self.rawsize(baserev)
2651 newlen = len(delta) - hlen
2661 newlen = len(delta) - hlen
2652 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2662 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2653 raise error.CensoredBaseError(self.indexfile,
2663 raise error.CensoredBaseError(self.indexfile,
2654 self.node(baserev))
2664 self.node(baserev))
2655
2665
2656 if not flags and self._peek_iscensored(baserev, delta, flush):
2666 if not flags and self._peek_iscensored(baserev, delta, flush):
2657 flags |= REVIDX_ISCENSORED
2667 flags |= REVIDX_ISCENSORED
2658
2668
2659 # We assume consumers of addrevisioncb will want to retrieve
2669 # We assume consumers of addrevisioncb will want to retrieve
2660 # the added revision, which will require a call to
2670 # the added revision, which will require a call to
2661 # revision(). revision() will fast path if there is a cache
2671 # revision(). revision() will fast path if there is a cache
2662 # hit. So, we tell _addrevision() to always cache in this case.
2672 # hit. So, we tell _addrevision() to always cache in this case.
2663 # We're only using addgroup() in the context of changegroup
2673 # We're only using addgroup() in the context of changegroup
2664 # generation so the revision data can always be handled as raw
2674 # generation so the revision data can always be handled as raw
2665 # by the flagprocessor.
2675 # by the flagprocessor.
2666 self._addrevision(node, None, transaction, link,
2676 self._addrevision(node, None, transaction, link,
2667 p1, p2, flags, (baserev, delta),
2677 p1, p2, flags, (baserev, delta),
2668 ifh, dfh,
2678 ifh, dfh,
2669 alwayscache=bool(addrevisioncb),
2679 alwayscache=bool(addrevisioncb),
2670 deltacomputer=deltacomputer)
2680 deltacomputer=deltacomputer)
2671
2681
2672 if addrevisioncb:
2682 if addrevisioncb:
2673 addrevisioncb(self, node)
2683 addrevisioncb(self, node)
2674
2684
2675 if not dfh and not self._inline:
2685 if not dfh and not self._inline:
2676 # addrevision switched from inline to conventional
2686 # addrevision switched from inline to conventional
2677 # reopen the index
2687 # reopen the index
2678 ifh.close()
2688 ifh.close()
2679 dfh = self._datafp("a+")
2689 dfh = self._datafp("a+")
2680 ifh = self._indexfp("a+")
2690 ifh = self._indexfp("a+")
2681 finally:
2691 finally:
2682 if dfh:
2692 if dfh:
2683 dfh.close()
2693 dfh.close()
2684 ifh.close()
2694 ifh.close()
2685
2695
2686 return nodes
2696 return nodes
2687
2697
2688 def iscensored(self, rev):
2698 def iscensored(self, rev):
2689 """Check if a file revision is censored."""
2699 """Check if a file revision is censored."""
2690 if not self._censorable:
2700 if not self._censorable:
2691 return False
2701 return False
2692
2702
2693 return self.flags(rev) & REVIDX_ISCENSORED
2703 return self.flags(rev) & REVIDX_ISCENSORED
2694
2704
2695 def _peek_iscensored(self, baserev, delta, flush):
2705 def _peek_iscensored(self, baserev, delta, flush):
2696 """Quickly check if a delta produces a censored revision."""
2706 """Quickly check if a delta produces a censored revision."""
2697 if not self._censorable:
2707 if not self._censorable:
2698 return False
2708 return False
2699
2709
2700 # Fragile heuristic: unless new file meta keys are added alphabetically
2710 # Fragile heuristic: unless new file meta keys are added alphabetically
2701 # preceding "censored", all censored revisions are prefixed by
2711 # preceding "censored", all censored revisions are prefixed by
2702 # "\1\ncensored:". A delta producing such a censored revision must be a
2712 # "\1\ncensored:". A delta producing such a censored revision must be a
2703 # full-replacement delta, so we inspect the first and only patch in the
2713 # full-replacement delta, so we inspect the first and only patch in the
2704 # delta for this prefix.
2714 # delta for this prefix.
2705 hlen = struct.calcsize(">lll")
2715 hlen = struct.calcsize(">lll")
2706 if len(delta) <= hlen:
2716 if len(delta) <= hlen:
2707 return False
2717 return False
2708
2718
2709 oldlen = self.rawsize(baserev)
2719 oldlen = self.rawsize(baserev)
2710 newlen = len(delta) - hlen
2720 newlen = len(delta) - hlen
2711 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2721 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2712 return False
2722 return False
2713
2723
2714 add = "\1\ncensored:"
2724 add = "\1\ncensored:"
2715 addlen = len(add)
2725 addlen = len(add)
2716 return newlen >= addlen and delta[hlen:hlen + addlen] == add
2726 return newlen >= addlen and delta[hlen:hlen + addlen] == add
2717
2727
2718 def getstrippoint(self, minlink):
2728 def getstrippoint(self, minlink):
2719 """find the minimum rev that must be stripped to strip the linkrev
2729 """find the minimum rev that must be stripped to strip the linkrev
2720
2730
2721 Returns a tuple containing the minimum rev and a set of all revs that
2731 Returns a tuple containing the minimum rev and a set of all revs that
2722 have linkrevs that will be broken by this strip.
2732 have linkrevs that will be broken by this strip.
2723 """
2733 """
2724 brokenrevs = set()
2734 brokenrevs = set()
2725 strippoint = len(self)
2735 strippoint = len(self)
2726
2736
2727 heads = {}
2737 heads = {}
2728 futurelargelinkrevs = set()
2738 futurelargelinkrevs = set()
2729 for head in self.headrevs():
2739 for head in self.headrevs():
2730 headlinkrev = self.linkrev(head)
2740 headlinkrev = self.linkrev(head)
2731 heads[head] = headlinkrev
2741 heads[head] = headlinkrev
2732 if headlinkrev >= minlink:
2742 if headlinkrev >= minlink:
2733 futurelargelinkrevs.add(headlinkrev)
2743 futurelargelinkrevs.add(headlinkrev)
2734
2744
2735 # This algorithm involves walking down the rev graph, starting at the
2745 # This algorithm involves walking down the rev graph, starting at the
2736 # heads. Since the revs are topologically sorted according to linkrev,
2746 # heads. Since the revs are topologically sorted according to linkrev,
2737 # once all head linkrevs are below the minlink, we know there are
2747 # once all head linkrevs are below the minlink, we know there are
2738 # no more revs that could have a linkrev greater than minlink.
2748 # no more revs that could have a linkrev greater than minlink.
2739 # So we can stop walking.
2749 # So we can stop walking.
2740 while futurelargelinkrevs:
2750 while futurelargelinkrevs:
2741 strippoint -= 1
2751 strippoint -= 1
2742 linkrev = heads.pop(strippoint)
2752 linkrev = heads.pop(strippoint)
2743
2753
2744 if linkrev < minlink:
2754 if linkrev < minlink:
2745 brokenrevs.add(strippoint)
2755 brokenrevs.add(strippoint)
2746 else:
2756 else:
2747 futurelargelinkrevs.remove(linkrev)
2757 futurelargelinkrevs.remove(linkrev)
2748
2758
2749 for p in self.parentrevs(strippoint):
2759 for p in self.parentrevs(strippoint):
2750 if p != nullrev:
2760 if p != nullrev:
2751 plinkrev = self.linkrev(p)
2761 plinkrev = self.linkrev(p)
2752 heads[p] = plinkrev
2762 heads[p] = plinkrev
2753 if plinkrev >= minlink:
2763 if plinkrev >= minlink:
2754 futurelargelinkrevs.add(plinkrev)
2764 futurelargelinkrevs.add(plinkrev)
2755
2765
2756 return strippoint, brokenrevs
2766 return strippoint, brokenrevs
2757
2767
2758 def strip(self, minlink, transaction):
2768 def strip(self, minlink, transaction):
2759 """truncate the revlog on the first revision with a linkrev >= minlink
2769 """truncate the revlog on the first revision with a linkrev >= minlink
2760
2770
2761 This function is called when we're stripping revision minlink and
2771 This function is called when we're stripping revision minlink and
2762 its descendants from the repository.
2772 its descendants from the repository.
2763
2773
2764 We have to remove all revisions with linkrev >= minlink, because
2774 We have to remove all revisions with linkrev >= minlink, because
2765 the equivalent changelog revisions will be renumbered after the
2775 the equivalent changelog revisions will be renumbered after the
2766 strip.
2776 strip.
2767
2777
2768 So we truncate the revlog on the first of these revisions, and
2778 So we truncate the revlog on the first of these revisions, and
2769 trust that the caller has saved the revisions that shouldn't be
2779 trust that the caller has saved the revisions that shouldn't be
2770 removed and that it'll re-add them after this truncation.
2780 removed and that it'll re-add them after this truncation.
2771 """
2781 """
2772 if len(self) == 0:
2782 if len(self) == 0:
2773 return
2783 return
2774
2784
2775 rev, _ = self.getstrippoint(minlink)
2785 rev, _ = self.getstrippoint(minlink)
2776 if rev == len(self):
2786 if rev == len(self):
2777 return
2787 return
2778
2788
2779 # first truncate the files on disk
2789 # first truncate the files on disk
2780 end = self.start(rev)
2790 end = self.start(rev)
2781 if not self._inline:
2791 if not self._inline:
2782 transaction.add(self.datafile, end)
2792 transaction.add(self.datafile, end)
2783 end = rev * self._io.size
2793 end = rev * self._io.size
2784 else:
2794 else:
2785 end += rev * self._io.size
2795 end += rev * self._io.size
2786
2796
2787 transaction.add(self.indexfile, end)
2797 transaction.add(self.indexfile, end)
2788
2798
2789 # then reset internal state in memory to forget those revisions
2799 # then reset internal state in memory to forget those revisions
2790 self._cache = None
2800 self._cache = None
2791 self._chaininfocache = {}
2801 self._chaininfocache = {}
2792 self._chunkclear()
2802 self._chunkclear()
2793 for x in pycompat.xrange(rev, len(self)):
2803 for x in pycompat.xrange(rev, len(self)):
2794 del self.nodemap[self.node(x)]
2804 del self.nodemap[self.node(x)]
2795
2805
2796 del self.index[rev:-1]
2806 del self.index[rev:-1]
2797 self._nodepos = None
2807 self._nodepos = None
2798
2808
2799 def checksize(self):
2809 def checksize(self):
2800 expected = 0
2810 expected = 0
2801 if len(self):
2811 if len(self):
2802 expected = max(0, self.end(len(self) - 1))
2812 expected = max(0, self.end(len(self) - 1))
2803
2813
2804 try:
2814 try:
2805 with self._datafp() as f:
2815 with self._datafp() as f:
2806 f.seek(0, 2)
2816 f.seek(0, 2)
2807 actual = f.tell()
2817 actual = f.tell()
2808 dd = actual - expected
2818 dd = actual - expected
2809 except IOError as inst:
2819 except IOError as inst:
2810 if inst.errno != errno.ENOENT:
2820 if inst.errno != errno.ENOENT:
2811 raise
2821 raise
2812 dd = 0
2822 dd = 0
2813
2823
2814 try:
2824 try:
2815 f = self.opener(self.indexfile)
2825 f = self.opener(self.indexfile)
2816 f.seek(0, 2)
2826 f.seek(0, 2)
2817 actual = f.tell()
2827 actual = f.tell()
2818 f.close()
2828 f.close()
2819 s = self._io.size
2829 s = self._io.size
2820 i = max(0, actual // s)
2830 i = max(0, actual // s)
2821 di = actual - (i * s)
2831 di = actual - (i * s)
2822 if self._inline:
2832 if self._inline:
2823 databytes = 0
2833 databytes = 0
2824 for r in self:
2834 for r in self:
2825 databytes += max(0, self.length(r))
2835 databytes += max(0, self.length(r))
2826 dd = 0
2836 dd = 0
2827 di = actual - len(self) * s - databytes
2837 di = actual - len(self) * s - databytes
2828 except IOError as inst:
2838 except IOError as inst:
2829 if inst.errno != errno.ENOENT:
2839 if inst.errno != errno.ENOENT:
2830 raise
2840 raise
2831 di = 0
2841 di = 0
2832
2842
2833 return (dd, di)
2843 return (dd, di)
2834
2844
2835 def files(self):
2845 def files(self):
2836 res = [self.indexfile]
2846 res = [self.indexfile]
2837 if not self._inline:
2847 if not self._inline:
2838 res.append(self.datafile)
2848 res.append(self.datafile)
2839 return res
2849 return res
2840
2850
2841 DELTAREUSEALWAYS = 'always'
2851 DELTAREUSEALWAYS = 'always'
2842 DELTAREUSESAMEREVS = 'samerevs'
2852 DELTAREUSESAMEREVS = 'samerevs'
2843 DELTAREUSENEVER = 'never'
2853 DELTAREUSENEVER = 'never'
2844
2854
2845 DELTAREUSEFULLADD = 'fulladd'
2855 DELTAREUSEFULLADD = 'fulladd'
2846
2856
2847 DELTAREUSEALL = {'always', 'samerevs', 'never', 'fulladd'}
2857 DELTAREUSEALL = {'always', 'samerevs', 'never', 'fulladd'}
2848
2858
2849 def clone(self, tr, destrevlog, addrevisioncb=None,
2859 def clone(self, tr, destrevlog, addrevisioncb=None,
2850 deltareuse=DELTAREUSESAMEREVS, deltabothparents=None):
2860 deltareuse=DELTAREUSESAMEREVS, deltabothparents=None):
2851 """Copy this revlog to another, possibly with format changes.
2861 """Copy this revlog to another, possibly with format changes.
2852
2862
2853 The destination revlog will contain the same revisions and nodes.
2863 The destination revlog will contain the same revisions and nodes.
2854 However, it may not be bit-for-bit identical due to e.g. delta encoding
2864 However, it may not be bit-for-bit identical due to e.g. delta encoding
2855 differences.
2865 differences.
2856
2866
2857 The ``deltareuse`` argument control how deltas from the existing revlog
2867 The ``deltareuse`` argument control how deltas from the existing revlog
2858 are preserved in the destination revlog. The argument can have the
2868 are preserved in the destination revlog. The argument can have the
2859 following values:
2869 following values:
2860
2870
2861 DELTAREUSEALWAYS
2871 DELTAREUSEALWAYS
2862 Deltas will always be reused (if possible), even if the destination
2872 Deltas will always be reused (if possible), even if the destination
2863 revlog would not select the same revisions for the delta. This is the
2873 revlog would not select the same revisions for the delta. This is the
2864 fastest mode of operation.
2874 fastest mode of operation.
2865 DELTAREUSESAMEREVS
2875 DELTAREUSESAMEREVS
2866 Deltas will be reused if the destination revlog would pick the same
2876 Deltas will be reused if the destination revlog would pick the same
2867 revisions for the delta. This mode strikes a balance between speed
2877 revisions for the delta. This mode strikes a balance between speed
2868 and optimization.
2878 and optimization.
2869 DELTAREUSENEVER
2879 DELTAREUSENEVER
2870 Deltas will never be reused. This is the slowest mode of execution.
2880 Deltas will never be reused. This is the slowest mode of execution.
2871 This mode can be used to recompute deltas (e.g. if the diff/delta
2881 This mode can be used to recompute deltas (e.g. if the diff/delta
2872 algorithm changes).
2882 algorithm changes).
2873
2883
2874 Delta computation can be slow, so the choice of delta reuse policy can
2884 Delta computation can be slow, so the choice of delta reuse policy can
2875 significantly affect run time.
2885 significantly affect run time.
2876
2886
2877 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2887 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2878 two extremes. Deltas will be reused if they are appropriate. But if the
2888 two extremes. Deltas will be reused if they are appropriate. But if the
2879 delta could choose a better revision, it will do so. This means if you
2889 delta could choose a better revision, it will do so. This means if you
2880 are converting a non-generaldelta revlog to a generaldelta revlog,
2890 are converting a non-generaldelta revlog to a generaldelta revlog,
2881 deltas will be recomputed if the delta's parent isn't a parent of the
2891 deltas will be recomputed if the delta's parent isn't a parent of the
2882 revision.
2892 revision.
2883
2893
2884 In addition to the delta policy, the ``deltabothparents`` argument
2894 In addition to the delta policy, the ``deltabothparents`` argument
2885 controls whether to compute deltas against both parents for merges.
2895 controls whether to compute deltas against both parents for merges.
2886 By default, the current default is used.
2896 By default, the current default is used.
2887 """
2897 """
2888 if deltareuse not in self.DELTAREUSEALL:
2898 if deltareuse not in self.DELTAREUSEALL:
2889 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2899 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2890
2900
2891 if len(destrevlog):
2901 if len(destrevlog):
2892 raise ValueError(_('destination revlog is not empty'))
2902 raise ValueError(_('destination revlog is not empty'))
2893
2903
2894 if getattr(self, 'filteredrevs', None):
2904 if getattr(self, 'filteredrevs', None):
2895 raise ValueError(_('source revlog has filtered revisions'))
2905 raise ValueError(_('source revlog has filtered revisions'))
2896 if getattr(destrevlog, 'filteredrevs', None):
2906 if getattr(destrevlog, 'filteredrevs', None):
2897 raise ValueError(_('destination revlog has filtered revisions'))
2907 raise ValueError(_('destination revlog has filtered revisions'))
2898
2908
2899 # lazydeltabase controls whether to reuse a cached delta, if possible.
2909 # lazydeltabase controls whether to reuse a cached delta, if possible.
2900 oldlazydeltabase = destrevlog._lazydeltabase
2910 oldlazydeltabase = destrevlog._lazydeltabase
2901 oldamd = destrevlog._deltabothparents
2911 oldamd = destrevlog._deltabothparents
2902
2912
2903 try:
2913 try:
2904 if deltareuse == self.DELTAREUSEALWAYS:
2914 if deltareuse == self.DELTAREUSEALWAYS:
2905 destrevlog._lazydeltabase = True
2915 destrevlog._lazydeltabase = True
2906 elif deltareuse == self.DELTAREUSESAMEREVS:
2916 elif deltareuse == self.DELTAREUSESAMEREVS:
2907 destrevlog._lazydeltabase = False
2917 destrevlog._lazydeltabase = False
2908
2918
2909 destrevlog._deltabothparents = deltabothparents or oldamd
2919 destrevlog._deltabothparents = deltabothparents or oldamd
2910
2920
2911 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2921 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2912 self.DELTAREUSESAMEREVS)
2922 self.DELTAREUSESAMEREVS)
2913
2923
2914 deltacomputer = _deltacomputer(destrevlog)
2924 deltacomputer = _deltacomputer(destrevlog)
2915 index = self.index
2925 index = self.index
2916 for rev in self:
2926 for rev in self:
2917 entry = index[rev]
2927 entry = index[rev]
2918
2928
2919 # Some classes override linkrev to take filtered revs into
2929 # Some classes override linkrev to take filtered revs into
2920 # account. Use raw entry from index.
2930 # account. Use raw entry from index.
2921 flags = entry[0] & 0xffff
2931 flags = entry[0] & 0xffff
2922 linkrev = entry[4]
2932 linkrev = entry[4]
2923 p1 = index[entry[5]][7]
2933 p1 = index[entry[5]][7]
2924 p2 = index[entry[6]][7]
2934 p2 = index[entry[6]][7]
2925 node = entry[7]
2935 node = entry[7]
2926
2936
2927 # (Possibly) reuse the delta from the revlog if allowed and
2937 # (Possibly) reuse the delta from the revlog if allowed and
2928 # the revlog chunk is a delta.
2938 # the revlog chunk is a delta.
2929 cachedelta = None
2939 cachedelta = None
2930 rawtext = None
2940 rawtext = None
2931 if populatecachedelta:
2941 if populatecachedelta:
2932 dp = self.deltaparent(rev)
2942 dp = self.deltaparent(rev)
2933 if dp != nullrev:
2943 if dp != nullrev:
2934 cachedelta = (dp, bytes(self._chunk(rev)))
2944 cachedelta = (dp, bytes(self._chunk(rev)))
2935
2945
2936 if not cachedelta:
2946 if not cachedelta:
2937 rawtext = self.revision(rev, raw=True)
2947 rawtext = self.revision(rev, raw=True)
2938
2948
2939
2949
2940 if deltareuse == self.DELTAREUSEFULLADD:
2950 if deltareuse == self.DELTAREUSEFULLADD:
2941 destrevlog.addrevision(rawtext, tr, linkrev, p1, p2,
2951 destrevlog.addrevision(rawtext, tr, linkrev, p1, p2,
2942 cachedelta=cachedelta,
2952 cachedelta=cachedelta,
2943 node=node, flags=flags,
2953 node=node, flags=flags,
2944 deltacomputer=deltacomputer)
2954 deltacomputer=deltacomputer)
2945 else:
2955 else:
2946 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2956 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2947 checkambig=False)
2957 checkambig=False)
2948 dfh = None
2958 dfh = None
2949 if not destrevlog._inline:
2959 if not destrevlog._inline:
2950 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2960 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2951 try:
2961 try:
2952 destrevlog._addrevision(node, rawtext, tr, linkrev, p1,
2962 destrevlog._addrevision(node, rawtext, tr, linkrev, p1,
2953 p2, flags, cachedelta, ifh, dfh,
2963 p2, flags, cachedelta, ifh, dfh,
2954 deltacomputer=deltacomputer)
2964 deltacomputer=deltacomputer)
2955 finally:
2965 finally:
2956 if dfh:
2966 if dfh:
2957 dfh.close()
2967 dfh.close()
2958 ifh.close()
2968 ifh.close()
2959
2969
2960 if addrevisioncb:
2970 if addrevisioncb:
2961 addrevisioncb(self, rev, node)
2971 addrevisioncb(self, rev, node)
2962 finally:
2972 finally:
2963 destrevlog._lazydeltabase = oldlazydeltabase
2973 destrevlog._lazydeltabase = oldlazydeltabase
2964 destrevlog._deltabothparents = oldamd
2974 destrevlog._deltabothparents = oldamd
@@ -1,402 +1,402
1 #require no-reposimplestore
1 #require no-reposimplestore
2
2
3 Check whether size of generaldelta revlog is not bigger than its
3 Check whether size of generaldelta revlog is not bigger than its
4 regular equivalent. Test would fail if generaldelta was naive
4 regular equivalent. Test would fail if generaldelta was naive
5 implementation of parentdelta: third manifest revision would be fully
5 implementation of parentdelta: third manifest revision would be fully
6 inserted due to big distance from its paren revision (zero).
6 inserted due to big distance from its paren revision (zero).
7
7
8 $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
8 $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
9 $ cd repo
9 $ cd repo
10 $ echo foo > foo
10 $ echo foo > foo
11 $ echo bar > bar
11 $ echo bar > bar
12 $ echo baz > baz
12 $ echo baz > baz
13 $ hg commit -q -Am boo
13 $ hg commit -q -Am boo
14 $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
14 $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
15 $ for r in 1 2 3; do
15 $ for r in 1 2 3; do
16 > echo $r > foo
16 > echo $r > foo
17 > hg commit -q -m $r
17 > hg commit -q -m $r
18 > hg up -q -r 0
18 > hg up -q -r 0
19 > hg pull . -q -r $r -R ../gdrepo
19 > hg pull . -q -r $r -R ../gdrepo
20 > done
20 > done
21
21
22 $ cd ..
22 $ cd ..
23 >>> from __future__ import print_function
23 >>> from __future__ import print_function
24 >>> import os
24 >>> import os
25 >>> regsize = os.stat("repo/.hg/store/00manifest.i").st_size
25 >>> regsize = os.stat("repo/.hg/store/00manifest.i").st_size
26 >>> gdsize = os.stat("gdrepo/.hg/store/00manifest.i").st_size
26 >>> gdsize = os.stat("gdrepo/.hg/store/00manifest.i").st_size
27 >>> if regsize < gdsize:
27 >>> if regsize < gdsize:
28 ... print('generaldata increased size of manifest')
28 ... print('generaldata increased size of manifest')
29
29
30 Verify rev reordering doesnt create invalid bundles (issue4462)
30 Verify rev reordering doesnt create invalid bundles (issue4462)
31 This requires a commit tree that when pulled will reorder manifest revs such
31 This requires a commit tree that when pulled will reorder manifest revs such
32 that the second manifest to create a file rev will be ordered before the first
32 that the second manifest to create a file rev will be ordered before the first
33 manifest to create that file rev. We also need to do a partial pull to ensure
33 manifest to create that file rev. We also need to do a partial pull to ensure
34 reordering happens. At the end we verify the linkrev points at the earliest
34 reordering happens. At the end we verify the linkrev points at the earliest
35 commit.
35 commit.
36
36
37 $ hg init server --config format.generaldelta=True
37 $ hg init server --config format.generaldelta=True
38 $ cd server
38 $ cd server
39 $ touch a
39 $ touch a
40 $ hg commit -Aqm a
40 $ hg commit -Aqm a
41 $ echo x > x
41 $ echo x > x
42 $ echo y > y
42 $ echo y > y
43 $ hg commit -Aqm xy
43 $ hg commit -Aqm xy
44 $ hg up -q '.^'
44 $ hg up -q '.^'
45 $ echo x > x
45 $ echo x > x
46 $ echo z > z
46 $ echo z > z
47 $ hg commit -Aqm xz
47 $ hg commit -Aqm xz
48 $ hg up -q 1
48 $ hg up -q 1
49 $ echo b > b
49 $ echo b > b
50 $ hg commit -Aqm b
50 $ hg commit -Aqm b
51 $ hg merge -q 2
51 $ hg merge -q 2
52 $ hg commit -Aqm merge
52 $ hg commit -Aqm merge
53 $ echo c > c
53 $ echo c > c
54 $ hg commit -Aqm c
54 $ hg commit -Aqm c
55 $ hg log -G -T '{rev} {shortest(node)} {desc}'
55 $ hg log -G -T '{rev} {shortest(node)} {desc}'
56 @ 5 ebb8 c
56 @ 5 ebb8 c
57 |
57 |
58 o 4 baf7 merge
58 o 4 baf7 merge
59 |\
59 |\
60 | o 3 a129 b
60 | o 3 a129 b
61 | |
61 | |
62 o | 2 958c xz
62 o | 2 958c xz
63 | |
63 | |
64 | o 1 f00c xy
64 | o 1 f00c xy
65 |/
65 |/
66 o 0 3903 a
66 o 0 3903 a
67
67
68 $ cd ..
68 $ cd ..
69 $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
69 $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
70 $ cd client
70 $ cd client
71 $ hg pull -q ../server -r 4
71 $ hg pull -q ../server -r 4
72 $ hg debugdeltachain x
72 $ hg debugdeltachain x
73 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
73 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
74 0 1 1 -1 base 3 2 3 1.50000 3 0 0.00000
74 0 1 1 -1 base 3 2 3 1.50000 3 0 0.00000
75
75
76 $ cd ..
76 $ cd ..
77
77
78 Test "usegeneraldelta" config
78 Test "usegeneraldelta" config
79 (repo are general delta, but incoming bundle are not re-deltafied)
79 (repo are general delta, but incoming bundle are not re-deltafied)
80
80
81 delta coming from the server base delta server are not recompressed.
81 delta coming from the server base delta server are not recompressed.
82 (also include the aggressive version for comparison)
82 (also include the aggressive version for comparison)
83
83
84 $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
84 $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
85 requesting all changes
85 requesting all changes
86 adding changesets
86 adding changesets
87 adding manifests
87 adding manifests
88 adding file changes
88 adding file changes
89 added 4 changesets with 6 changes to 3 files (+2 heads)
89 added 4 changesets with 6 changes to 3 files (+2 heads)
90 new changesets 0ea3fcf9d01d:bba78d330d9c
90 new changesets 0ea3fcf9d01d:bba78d330d9c
91 updating to branch default
91 updating to branch default
92 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 $ hg clone repo --pull --config format.generaldelta=1 full
93 $ hg clone repo --pull --config format.generaldelta=1 full
94 requesting all changes
94 requesting all changes
95 adding changesets
95 adding changesets
96 adding manifests
96 adding manifests
97 adding file changes
97 adding file changes
98 added 4 changesets with 6 changes to 3 files (+2 heads)
98 added 4 changesets with 6 changes to 3 files (+2 heads)
99 new changesets 0ea3fcf9d01d:bba78d330d9c
99 new changesets 0ea3fcf9d01d:bba78d330d9c
100 updating to branch default
100 updating to branch default
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ hg -R repo debugdeltachain -m
102 $ hg -R repo debugdeltachain -m
103 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
103 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
104 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
104 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
105 1 1 2 0 prev 57 135 161 1.19259 161 0 0.00000
105 1 1 2 0 prev 57 135 161 1.19259 161 0 0.00000
106 2 1 3 1 prev 57 135 218 1.61481 218 0 0.00000
106 2 1 3 1 prev 57 135 218 1.61481 218 0 0.00000
107 3 2 1 -1 base 104 135 104 0.77037 104 0 0.00000
107 3 2 1 -1 base 104 135 104 0.77037 104 0 0.00000
108 $ hg -R usegd debugdeltachain -m
108 $ hg -R usegd debugdeltachain -m
109 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
109 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
110 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
110 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
111 1 1 2 0 p1 57 135 161 1.19259 161 0 0.00000
111 1 1 2 0 p1 57 135 161 1.19259 161 0 0.00000
112 2 1 3 1 prev 57 135 218 1.61481 218 0 0.00000
112 2 1 3 1 prev 57 135 218 1.61481 218 0 0.00000
113 3 1 2 0 p1 57 135 161 1.19259 275 114 0.70807
113 3 1 2 0 p1 57 135 161 1.19259 275 114 0.70807
114 $ hg -R full debugdeltachain -m
114 $ hg -R full debugdeltachain -m
115 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
115 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
116 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
116 0 1 1 -1 base 104 135 104 0.77037 104 0 0.00000
117 1 1 2 0 p1 57 135 161 1.19259 161 0 0.00000
117 1 1 2 0 p1 57 135 161 1.19259 161 0 0.00000
118 2 1 2 0 p1 57 135 161 1.19259 218 57 0.35404
118 2 1 2 0 p1 57 135 161 1.19259 218 57 0.35404
119 3 1 2 0 p1 57 135 161 1.19259 275 114 0.70807
119 3 1 2 0 p1 57 135 161 1.19259 275 114 0.70807
120
120
121 Test revlog.optimize-delta-parent-choice
121 Test revlog.optimize-delta-parent-choice
122
122
123 $ hg init --config format.generaldelta=1 aggressive
123 $ hg init --config format.generaldelta=1 aggressive
124 $ cd aggressive
124 $ cd aggressive
125 $ cat << EOF >> .hg/hgrc
125 $ cat << EOF >> .hg/hgrc
126 > [format]
126 > [format]
127 > generaldelta = 1
127 > generaldelta = 1
128 > EOF
128 > EOF
129 $ touch a b c d e
129 $ touch a b c d e
130 $ hg commit -Aqm side1
130 $ hg commit -Aqm side1
131 $ hg up -q null
131 $ hg up -q null
132 $ touch x y
132 $ touch x y
133 $ hg commit -Aqm side2
133 $ hg commit -Aqm side2
134
134
135 - Verify non-aggressive merge uses p1 (commit 1) as delta parent
135 - Verify non-aggressive merge uses p1 (commit 1) as delta parent
136 $ hg merge -q 0
136 $ hg merge -q 0
137 $ hg commit -q -m merge
137 $ hg commit -q -m merge
138 $ hg debugdeltachain -m
138 $ hg debugdeltachain -m
139 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
139 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
140 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000
140 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000
141 1 1 2 0 prev 61 86 120 1.39535 120 0 0.00000
141 1 1 2 0 prev 61 86 120 1.39535 120 0 0.00000
142 2 1 2 0 p2 62 301 121 0.40199 182 61 0.50413
142 2 1 2 0 p2 62 301 121 0.40199 182 61 0.50413
143
143
144 $ hg strip -q -r . --config extensions.strip=
144 $ hg strip -q -r . --config extensions.strip=
145
145
146 - Verify aggressive merge uses p2 (commit 0) as delta parent
146 - Verify aggressive merge uses p2 (commit 0) as delta parent
147 $ hg up -q -C 1
147 $ hg up -q -C 1
148 $ hg merge -q 0
148 $ hg merge -q 0
149 $ hg commit -q -m merge --config storage.revlog.optimize-delta-parent-choice=yes
149 $ hg commit -q -m merge --config storage.revlog.optimize-delta-parent-choice=yes
150 $ hg debugdeltachain -m
150 $ hg debugdeltachain -m
151 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
151 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
152 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000
152 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000
153 1 1 2 0 prev 61 86 120 1.39535 120 0 0.00000
153 1 1 2 0 prev 61 86 120 1.39535 120 0 0.00000
154 2 1 2 0 p2 62 301 121 0.40199 182 61 0.50413
154 2 1 2 0 p2 62 301 121 0.40199 182 61 0.50413
155
155
156 Test that strip bundle use bundle2
156 Test that strip bundle use bundle2
157 $ hg --config extensions.strip= strip .
157 $ hg --config extensions.strip= strip .
158 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
158 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
159 saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg
159 saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg
160 $ hg debugbundle .hg/strip-backup/*
160 $ hg debugbundle .hg/strip-backup/*
161 Stream params: {Compression: BZ}
161 Stream params: {Compression: BZ}
162 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
162 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
163 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
163 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
164 cache:rev-branch-cache -- {} (mandatory: False)
164 cache:rev-branch-cache -- {} (mandatory: False)
165 phase-heads -- {} (mandatory: True)
165 phase-heads -- {} (mandatory: True)
166 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9 draft
166 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9 draft
167
167
168 $ cd ..
168 $ cd ..
169
169
170 test maxdeltachainspan
170 test maxdeltachainspan
171
171
172 $ hg init source-repo
172 $ hg init source-repo
173 $ cd source-repo
173 $ cd source-repo
174 $ hg debugbuilddag --new-file '.+5:brancha$.+11:branchb$.+30:branchc<brancha+2<branchb+2'
174 $ hg debugbuilddag --new-file '.+5:brancha$.+11:branchb$.+30:branchc<brancha+2<branchb+2'
175 # add an empty revision somewhere
175 # add an empty revision somewhere
176 $ hg up tip
176 $ hg up tip
177 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 $ hg rm .
178 $ hg rm .
179 removing nf10
179 removing nf10
180 removing nf11
180 removing nf11
181 removing nf12
181 removing nf12
182 removing nf13
182 removing nf13
183 removing nf14
183 removing nf14
184 removing nf15
184 removing nf15
185 removing nf16
185 removing nf16
186 removing nf17
186 removing nf17
187 removing nf51
187 removing nf51
188 removing nf52
188 removing nf52
189 removing nf6
189 removing nf6
190 removing nf7
190 removing nf7
191 removing nf8
191 removing nf8
192 removing nf9
192 removing nf9
193 $ hg commit -m 'empty all'
193 $ hg commit -m 'empty all'
194 $ hg revert --all --rev 'p1(.)'
194 $ hg revert --all --rev 'p1(.)'
195 adding nf10
195 adding nf10
196 adding nf11
196 adding nf11
197 adding nf12
197 adding nf12
198 adding nf13
198 adding nf13
199 adding nf14
199 adding nf14
200 adding nf15
200 adding nf15
201 adding nf16
201 adding nf16
202 adding nf17
202 adding nf17
203 adding nf51
203 adding nf51
204 adding nf52
204 adding nf52
205 adding nf6
205 adding nf6
206 adding nf7
206 adding nf7
207 adding nf8
207 adding nf8
208 adding nf9
208 adding nf9
209 $ hg commit -m 'restore all'
209 $ hg commit -m 'restore all'
210 $ hg up null
210 $ hg up null
211 0 files updated, 0 files merged, 14 files removed, 0 files unresolved
211 0 files updated, 0 files merged, 14 files removed, 0 files unresolved
212 $
212 $
213 $ cd ..
213 $ cd ..
214 $ hg -R source-repo debugdeltachain -m
214 $ hg -R source-repo debugdeltachain -m
215 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
215 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
216 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
216 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
217 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
217 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
218 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
218 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
219 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
219 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
220 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
220 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
221 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
221 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
222 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
222 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
223 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
223 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
224 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
224 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
225 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
225 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
226 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
226 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
227 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
227 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
228 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
228 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
229 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
229 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
230 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
230 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
231 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
231 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
232 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
232 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
233 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
233 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
234 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
234 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
235 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
235 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
236 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
236 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
237 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
237 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
238 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
238 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
239 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
239 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
240 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
240 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
241 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
241 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
242 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
242 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
243 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
243 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
244 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
244 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
245 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
245 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
246 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
246 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
247 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
247 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
248 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
248 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
249 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
249 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
250 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
250 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
251 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
251 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
252 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
252 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
253 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
253 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
254 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
254 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
255 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
255 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
256 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
256 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
257 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
257 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
258 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
258 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
259 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
259 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
260 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
260 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
261 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
261 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
262 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
262 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
263 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
263 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
264 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
264 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
265 49 4 1 -1 base 197 316 197 0.62342 197 0 0.00000
265 49 4 1 -1 base 197 316 197 0.62342 197 0 0.00000
266 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000
266 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000
267 51 4 3 50 prev 356 594 611 1.02862 611 0 0.00000
267 51 4 3 50 prev 356 594 611 1.02862 611 0 0.00000
268 52 4 4 51 p1 58 640 669 1.04531 669 0 0.00000
268 52 4 4 51 p1 58 640 669 1.04531 669 0 0.00000
269 53 5 1 -1 base 0 0 0 0.00000 0 0 0.00000
269 53 5 1 -1 base 0 0 0 0.00000 0 0 0.00000
270 54 5 2 53 p1 376 640 376 0.58750 376 0 0.00000
270 54 6 1 -1 base 369 640 369 0.57656 369 0 0.00000
271 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes
271 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes
272 requesting all changes
272 requesting all changes
273 adding changesets
273 adding changesets
274 adding manifests
274 adding manifests
275 adding file changes
275 adding file changes
276 added 55 changesets with 53 changes to 53 files (+2 heads)
276 added 55 changesets with 53 changes to 53 files (+2 heads)
277 new changesets 61246295ee1e:c930ac4a5b32
277 new changesets 61246295ee1e:c930ac4a5b32
278 updating to branch default
278 updating to branch default
279 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 $ hg -R relax-chain debugdeltachain -m
280 $ hg -R relax-chain debugdeltachain -m
281 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
281 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
282 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
282 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
283 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
283 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
284 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
284 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
285 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
285 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
286 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
286 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
287 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
287 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
288 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
288 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
289 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
289 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
290 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
290 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
291 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
291 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
292 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
292 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
293 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
293 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
294 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
294 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
295 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
295 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
296 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
296 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
297 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
297 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
298 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
298 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
299 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
299 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
300 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
300 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
301 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
301 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
302 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
302 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
303 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
303 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
304 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
304 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
305 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
305 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
306 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
306 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
307 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
307 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
308 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
308 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
309 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
309 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
310 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
310 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
311 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
311 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
312 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
312 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
313 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
313 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
314 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
314 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
315 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
315 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
316 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
316 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
317 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
317 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
318 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
318 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
319 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
319 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
320 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
320 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
321 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
321 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
322 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
322 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
323 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
323 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
324 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
324 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
325 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
325 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
326 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
326 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
327 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
327 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
328 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
328 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
329 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
329 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
330 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
330 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
331 49 4 1 -1 base 197 316 197 0.62342 197 0 0.00000
331 49 4 1 -1 base 197 316 197 0.62342 197 0 0.00000
332 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000
332 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000
333 51 2 13 17 p1 58 594 739 1.24411 2781 2042 2.76319
333 51 2 13 17 p1 58 594 739 1.24411 2781 2042 2.76319
334 52 5 1 -1 base 369 640 369 0.57656 369 0 0.00000
334 52 5 1 -1 base 369 640 369 0.57656 369 0 0.00000
335 53 6 1 -1 base 0 0 0 0.00000 0 0 0.00000
335 53 6 1 -1 base 0 0 0 0.00000 0 0 0.00000
336 54 6 2 53 p1 376 640 376 0.58750 376 0 0.00000
336 54 7 1 -1 base 369 640 369 0.57656 369 0 0.00000
337 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=0 noconst-chain --config format.generaldelta=yes
337 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=0 noconst-chain --config format.generaldelta=yes
338 requesting all changes
338 requesting all changes
339 adding changesets
339 adding changesets
340 adding manifests
340 adding manifests
341 adding file changes
341 adding file changes
342 added 55 changesets with 53 changes to 53 files (+2 heads)
342 added 55 changesets with 53 changes to 53 files (+2 heads)
343 new changesets 61246295ee1e:c930ac4a5b32
343 new changesets 61246295ee1e:c930ac4a5b32
344 updating to branch default
344 updating to branch default
345 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 $ hg -R noconst-chain debugdeltachain -m
346 $ hg -R noconst-chain debugdeltachain -m
347 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
347 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
348 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
348 0 1 1 -1 base 46 45 46 1.02222 46 0 0.00000
349 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
349 1 1 2 0 p1 57 90 103 1.14444 103 0 0.00000
350 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
350 2 1 3 1 p1 57 135 160 1.18519 160 0 0.00000
351 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
351 3 1 4 2 p1 57 180 217 1.20556 217 0 0.00000
352 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
352 4 1 5 3 p1 57 225 274 1.21778 274 0 0.00000
353 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
353 5 1 6 4 p1 57 270 331 1.22593 331 0 0.00000
354 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
354 6 2 1 -1 base 46 45 46 1.02222 46 0 0.00000
355 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
355 7 2 2 6 p1 57 90 103 1.14444 103 0 0.00000
356 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
356 8 2 3 7 p1 57 135 160 1.18519 160 0 0.00000
357 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
357 9 2 4 8 p1 57 180 217 1.20556 217 0 0.00000
358 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
358 10 2 5 9 p1 58 226 275 1.21681 275 0 0.00000
359 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
359 11 2 6 10 p1 58 272 333 1.22426 333 0 0.00000
360 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
360 12 2 7 11 p1 58 318 391 1.22956 391 0 0.00000
361 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
361 13 2 8 12 p1 58 364 449 1.23352 449 0 0.00000
362 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
362 14 2 9 13 p1 58 410 507 1.23659 507 0 0.00000
363 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
363 15 2 10 14 p1 58 456 565 1.23904 565 0 0.00000
364 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
364 16 2 11 15 p1 58 502 623 1.24104 623 0 0.00000
365 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
365 17 2 12 16 p1 58 548 681 1.24270 681 0 0.00000
366 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
366 18 3 1 -1 base 47 46 47 1.02174 47 0 0.00000
367 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
367 19 3 2 18 p1 58 92 105 1.14130 105 0 0.00000
368 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
368 20 3 3 19 p1 58 138 163 1.18116 163 0 0.00000
369 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
369 21 3 4 20 p1 58 184 221 1.20109 221 0 0.00000
370 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
370 22 3 5 21 p1 58 230 279 1.21304 279 0 0.00000
371 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
371 23 3 6 22 p1 58 276 337 1.22101 337 0 0.00000
372 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
372 24 3 7 23 p1 58 322 395 1.22671 395 0 0.00000
373 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
373 25 3 8 24 p1 58 368 453 1.23098 453 0 0.00000
374 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
374 26 3 9 25 p1 58 414 511 1.23430 511 0 0.00000
375 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
375 27 3 10 26 p1 58 460 569 1.23696 569 0 0.00000
376 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
376 28 3 11 27 p1 58 506 627 1.23913 627 0 0.00000
377 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
377 29 3 12 28 p1 58 552 685 1.24094 685 0 0.00000
378 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
378 30 3 13 29 p1 58 598 743 1.24247 743 0 0.00000
379 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
379 31 3 14 30 p1 58 644 801 1.24379 801 0 0.00000
380 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
380 32 3 15 31 p1 58 690 859 1.24493 859 0 0.00000
381 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
381 33 3 16 32 p1 58 736 917 1.24592 917 0 0.00000
382 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
382 34 3 17 33 p1 58 782 975 1.24680 975 0 0.00000
383 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
383 35 3 18 34 p1 58 828 1033 1.24758 1033 0 0.00000
384 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
384 36 3 19 35 p1 58 874 1091 1.24828 1091 0 0.00000
385 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
385 37 3 20 36 p1 58 920 1149 1.24891 1149 0 0.00000
386 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
386 38 3 21 37 p1 58 966 1207 1.24948 1207 0 0.00000
387 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
387 39 3 22 38 p1 58 1012 1265 1.25000 1265 0 0.00000
388 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
388 40 3 23 39 p1 58 1058 1323 1.25047 1323 0 0.00000
389 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
389 41 3 24 40 p1 58 1104 1381 1.25091 1381 0 0.00000
390 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
390 42 3 25 41 p1 58 1150 1439 1.25130 1439 0 0.00000
391 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
391 43 3 26 42 p1 58 1196 1497 1.25167 1497 0 0.00000
392 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
392 44 3 27 43 p1 58 1242 1555 1.25201 1555 0 0.00000
393 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
393 45 3 28 44 p1 58 1288 1613 1.25233 1613 0 0.00000
394 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
394 46 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000
395 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
395 47 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000
396 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
396 48 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000
397 49 1 7 5 p1 58 316 389 1.23101 2857 2468 6.34447
397 49 1 7 5 p1 58 316 389 1.23101 2857 2468 6.34447
398 50 1 8 49 p1 58 362 447 1.23481 2915 2468 5.52125
398 50 1 8 49 p1 58 362 447 1.23481 2915 2468 5.52125
399 51 2 13 17 p1 58 594 739 1.24411 2642 1903 2.57510
399 51 2 13 17 p1 58 594 739 1.24411 2642 1903 2.57510
400 52 2 14 51 p1 58 640 797 1.24531 2700 1903 2.38770
400 52 2 14 51 p1 58 640 797 1.24531 2700 1903 2.38770
401 53 4 1 -1 base 0 0 0 0.00000 0 0 0.00000
401 53 4 1 -1 base 0 0 0 0.00000 0 0 0.00000
402 54 4 2 53 p1 376 640 376 0.58750 376 0 0.00000
402 54 5 1 -1 base 369 640 369 0.57656 369 0 0.00000
General Comments 0
You need to be logged in to leave comments. Login now