##// END OF EJS Templates
delta-find: move pre-filtering with other pre-filtering logic...
marmoute -
r50508:e706bb41 default
parent child Browse files
Show More
@@ -1,1396 +1,1398 b''
1 # revlogdeltas.py - Logic around delta computation for revlog
1 # revlogdeltas.py - Logic around delta computation for revlog
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 # Copyright 2018 Octobus <contact@octobus.net>
4 # Copyright 2018 Octobus <contact@octobus.net>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8 """Helper class to compute deltas stored inside revlogs"""
8 """Helper class to compute deltas stored inside revlogs"""
9
9
10
10
11 import collections
11 import collections
12 import struct
12 import struct
13
13
14 # import stuff from node for others to import from revlog
14 # import stuff from node for others to import from revlog
15 from ..node import nullrev
15 from ..node import nullrev
16 from ..i18n import _
16 from ..i18n import _
17 from ..pycompat import getattr
17 from ..pycompat import getattr
18
18
19 from .constants import (
19 from .constants import (
20 COMP_MODE_DEFAULT,
20 COMP_MODE_DEFAULT,
21 COMP_MODE_INLINE,
21 COMP_MODE_INLINE,
22 COMP_MODE_PLAIN,
22 COMP_MODE_PLAIN,
23 KIND_CHANGELOG,
23 KIND_CHANGELOG,
24 KIND_FILELOG,
24 KIND_FILELOG,
25 KIND_MANIFESTLOG,
25 KIND_MANIFESTLOG,
26 REVIDX_ISCENSORED,
26 REVIDX_ISCENSORED,
27 REVIDX_RAWTEXT_CHANGING_FLAGS,
27 REVIDX_RAWTEXT_CHANGING_FLAGS,
28 )
28 )
29
29
30 from ..thirdparty import attr
30 from ..thirdparty import attr
31
31
32 from .. import (
32 from .. import (
33 error,
33 error,
34 mdiff,
34 mdiff,
35 util,
35 util,
36 )
36 )
37
37
38 from . import flagutil
38 from . import flagutil
39
39
40 # maximum <delta-chain-data>/<revision-text-length> ratio
40 # maximum <delta-chain-data>/<revision-text-length> ratio
41 LIMIT_DELTA2TEXT = 2
41 LIMIT_DELTA2TEXT = 2
42
42
43
43
44 class _testrevlog:
44 class _testrevlog:
45 """minimalist fake revlog to use in doctests"""
45 """minimalist fake revlog to use in doctests"""
46
46
47 def __init__(self, data, density=0.5, mingap=0, snapshot=()):
47 def __init__(self, data, density=0.5, mingap=0, snapshot=()):
48 """data is an list of revision payload boundaries"""
48 """data is an list of revision payload boundaries"""
49 self._data = data
49 self._data = data
50 self._srdensitythreshold = density
50 self._srdensitythreshold = density
51 self._srmingapsize = mingap
51 self._srmingapsize = mingap
52 self._snapshot = set(snapshot)
52 self._snapshot = set(snapshot)
53 self.index = None
53 self.index = None
54
54
55 def start(self, rev):
55 def start(self, rev):
56 if rev == nullrev:
56 if rev == nullrev:
57 return 0
57 return 0
58 if rev == 0:
58 if rev == 0:
59 return 0
59 return 0
60 return self._data[rev - 1]
60 return self._data[rev - 1]
61
61
62 def end(self, rev):
62 def end(self, rev):
63 if rev == nullrev:
63 if rev == nullrev:
64 return 0
64 return 0
65 return self._data[rev]
65 return self._data[rev]
66
66
67 def length(self, rev):
67 def length(self, rev):
68 return self.end(rev) - self.start(rev)
68 return self.end(rev) - self.start(rev)
69
69
70 def __len__(self):
70 def __len__(self):
71 return len(self._data)
71 return len(self._data)
72
72
73 def issnapshot(self, rev):
73 def issnapshot(self, rev):
74 if rev == nullrev:
74 if rev == nullrev:
75 return True
75 return True
76 return rev in self._snapshot
76 return rev in self._snapshot
77
77
78
78
79 def slicechunk(revlog, revs, targetsize=None):
79 def slicechunk(revlog, revs, targetsize=None):
80 """slice revs to reduce the amount of unrelated data to be read from disk.
80 """slice revs to reduce the amount of unrelated data to be read from disk.
81
81
82 ``revs`` is sliced into groups that should be read in one time.
82 ``revs`` is sliced into groups that should be read in one time.
83 Assume that revs are sorted.
83 Assume that revs are sorted.
84
84
85 The initial chunk is sliced until the overall density (payload/chunks-span
85 The initial chunk is sliced until the overall density (payload/chunks-span
86 ratio) is above `revlog._srdensitythreshold`. No gap smaller than
86 ratio) is above `revlog._srdensitythreshold`. No gap smaller than
87 `revlog._srmingapsize` is skipped.
87 `revlog._srmingapsize` is skipped.
88
88
89 If `targetsize` is set, no chunk larger than `targetsize` will be yield.
89 If `targetsize` is set, no chunk larger than `targetsize` will be yield.
90 For consistency with other slicing choice, this limit won't go lower than
90 For consistency with other slicing choice, this limit won't go lower than
91 `revlog._srmingapsize`.
91 `revlog._srmingapsize`.
92
92
93 If individual revisions chunk are larger than this limit, they will still
93 If individual revisions chunk are larger than this limit, they will still
94 be raised individually.
94 be raised individually.
95
95
96 >>> data = [
96 >>> data = [
97 ... 5, #00 (5)
97 ... 5, #00 (5)
98 ... 10, #01 (5)
98 ... 10, #01 (5)
99 ... 12, #02 (2)
99 ... 12, #02 (2)
100 ... 12, #03 (empty)
100 ... 12, #03 (empty)
101 ... 27, #04 (15)
101 ... 27, #04 (15)
102 ... 31, #05 (4)
102 ... 31, #05 (4)
103 ... 31, #06 (empty)
103 ... 31, #06 (empty)
104 ... 42, #07 (11)
104 ... 42, #07 (11)
105 ... 47, #08 (5)
105 ... 47, #08 (5)
106 ... 47, #09 (empty)
106 ... 47, #09 (empty)
107 ... 48, #10 (1)
107 ... 48, #10 (1)
108 ... 51, #11 (3)
108 ... 51, #11 (3)
109 ... 74, #12 (23)
109 ... 74, #12 (23)
110 ... 85, #13 (11)
110 ... 85, #13 (11)
111 ... 86, #14 (1)
111 ... 86, #14 (1)
112 ... 91, #15 (5)
112 ... 91, #15 (5)
113 ... ]
113 ... ]
114 >>> revlog = _testrevlog(data, snapshot=range(16))
114 >>> revlog = _testrevlog(data, snapshot=range(16))
115
115
116 >>> list(slicechunk(revlog, list(range(16))))
116 >>> list(slicechunk(revlog, list(range(16))))
117 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
117 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
118 >>> list(slicechunk(revlog, [0, 15]))
118 >>> list(slicechunk(revlog, [0, 15]))
119 [[0], [15]]
119 [[0], [15]]
120 >>> list(slicechunk(revlog, [0, 11, 15]))
120 >>> list(slicechunk(revlog, [0, 11, 15]))
121 [[0], [11], [15]]
121 [[0], [11], [15]]
122 >>> list(slicechunk(revlog, [0, 11, 13, 15]))
122 >>> list(slicechunk(revlog, [0, 11, 13, 15]))
123 [[0], [11, 13, 15]]
123 [[0], [11, 13, 15]]
124 >>> list(slicechunk(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
124 >>> list(slicechunk(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
125 [[1, 2], [5, 8, 10, 11], [14]]
125 [[1, 2], [5, 8, 10, 11], [14]]
126
126
127 Slicing with a maximum chunk size
127 Slicing with a maximum chunk size
128 >>> list(slicechunk(revlog, [0, 11, 13, 15], targetsize=15))
128 >>> list(slicechunk(revlog, [0, 11, 13, 15], targetsize=15))
129 [[0], [11], [13], [15]]
129 [[0], [11], [13], [15]]
130 >>> list(slicechunk(revlog, [0, 11, 13, 15], targetsize=20))
130 >>> list(slicechunk(revlog, [0, 11, 13, 15], targetsize=20))
131 [[0], [11], [13, 15]]
131 [[0], [11], [13, 15]]
132
132
133 Slicing involving nullrev
133 Slicing involving nullrev
134 >>> list(slicechunk(revlog, [-1, 0, 11, 13, 15], targetsize=20))
134 >>> list(slicechunk(revlog, [-1, 0, 11, 13, 15], targetsize=20))
135 [[-1, 0], [11], [13, 15]]
135 [[-1, 0], [11], [13, 15]]
136 >>> list(slicechunk(revlog, [-1, 13, 15], targetsize=5))
136 >>> list(slicechunk(revlog, [-1, 13, 15], targetsize=5))
137 [[-1], [13], [15]]
137 [[-1], [13], [15]]
138 """
138 """
139 if targetsize is not None:
139 if targetsize is not None:
140 targetsize = max(targetsize, revlog._srmingapsize)
140 targetsize = max(targetsize, revlog._srmingapsize)
141 # targetsize should not be specified when evaluating delta candidates:
141 # targetsize should not be specified when evaluating delta candidates:
142 # * targetsize is used to ensure we stay within specification when reading,
142 # * targetsize is used to ensure we stay within specification when reading,
143 densityslicing = getattr(revlog.index, 'slicechunktodensity', None)
143 densityslicing = getattr(revlog.index, 'slicechunktodensity', None)
144 if densityslicing is None:
144 if densityslicing is None:
145 densityslicing = lambda x, y, z: _slicechunktodensity(revlog, x, y, z)
145 densityslicing = lambda x, y, z: _slicechunktodensity(revlog, x, y, z)
146 for chunk in densityslicing(
146 for chunk in densityslicing(
147 revs, revlog._srdensitythreshold, revlog._srmingapsize
147 revs, revlog._srdensitythreshold, revlog._srmingapsize
148 ):
148 ):
149 for subchunk in _slicechunktosize(revlog, chunk, targetsize):
149 for subchunk in _slicechunktosize(revlog, chunk, targetsize):
150 yield subchunk
150 yield subchunk
151
151
152
152
153 def _slicechunktosize(revlog, revs, targetsize=None):
153 def _slicechunktosize(revlog, revs, targetsize=None):
154 """slice revs to match the target size
154 """slice revs to match the target size
155
155
156 This is intended to be used on chunk that density slicing selected by that
156 This is intended to be used on chunk that density slicing selected by that
157 are still too large compared to the read garantee of revlog. This might
157 are still too large compared to the read garantee of revlog. This might
158 happens when "minimal gap size" interrupted the slicing or when chain are
158 happens when "minimal gap size" interrupted the slicing or when chain are
159 built in a way that create large blocks next to each other.
159 built in a way that create large blocks next to each other.
160
160
161 >>> data = [
161 >>> data = [
162 ... 3, #0 (3)
162 ... 3, #0 (3)
163 ... 5, #1 (2)
163 ... 5, #1 (2)
164 ... 6, #2 (1)
164 ... 6, #2 (1)
165 ... 8, #3 (2)
165 ... 8, #3 (2)
166 ... 8, #4 (empty)
166 ... 8, #4 (empty)
167 ... 11, #5 (3)
167 ... 11, #5 (3)
168 ... 12, #6 (1)
168 ... 12, #6 (1)
169 ... 13, #7 (1)
169 ... 13, #7 (1)
170 ... 14, #8 (1)
170 ... 14, #8 (1)
171 ... ]
171 ... ]
172
172
173 == All snapshots cases ==
173 == All snapshots cases ==
174 >>> revlog = _testrevlog(data, snapshot=range(9))
174 >>> revlog = _testrevlog(data, snapshot=range(9))
175
175
176 Cases where chunk is already small enough
176 Cases where chunk is already small enough
177 >>> list(_slicechunktosize(revlog, [0], 3))
177 >>> list(_slicechunktosize(revlog, [0], 3))
178 [[0]]
178 [[0]]
179 >>> list(_slicechunktosize(revlog, [6, 7], 3))
179 >>> list(_slicechunktosize(revlog, [6, 7], 3))
180 [[6, 7]]
180 [[6, 7]]
181 >>> list(_slicechunktosize(revlog, [0], None))
181 >>> list(_slicechunktosize(revlog, [0], None))
182 [[0]]
182 [[0]]
183 >>> list(_slicechunktosize(revlog, [6, 7], None))
183 >>> list(_slicechunktosize(revlog, [6, 7], None))
184 [[6, 7]]
184 [[6, 7]]
185
185
186 cases where we need actual slicing
186 cases where we need actual slicing
187 >>> list(_slicechunktosize(revlog, [0, 1], 3))
187 >>> list(_slicechunktosize(revlog, [0, 1], 3))
188 [[0], [1]]
188 [[0], [1]]
189 >>> list(_slicechunktosize(revlog, [1, 3], 3))
189 >>> list(_slicechunktosize(revlog, [1, 3], 3))
190 [[1], [3]]
190 [[1], [3]]
191 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
191 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
192 [[1, 2], [3]]
192 [[1, 2], [3]]
193 >>> list(_slicechunktosize(revlog, [3, 5], 3))
193 >>> list(_slicechunktosize(revlog, [3, 5], 3))
194 [[3], [5]]
194 [[3], [5]]
195 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
195 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
196 [[3], [5]]
196 [[3], [5]]
197 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
197 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
198 [[5], [6, 7, 8]]
198 [[5], [6, 7, 8]]
199 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
199 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
200 [[0], [1, 2], [3], [5], [6, 7, 8]]
200 [[0], [1, 2], [3], [5], [6, 7, 8]]
201
201
202 Case with too large individual chunk (must return valid chunk)
202 Case with too large individual chunk (must return valid chunk)
203 >>> list(_slicechunktosize(revlog, [0, 1], 2))
203 >>> list(_slicechunktosize(revlog, [0, 1], 2))
204 [[0], [1]]
204 [[0], [1]]
205 >>> list(_slicechunktosize(revlog, [1, 3], 1))
205 >>> list(_slicechunktosize(revlog, [1, 3], 1))
206 [[1], [3]]
206 [[1], [3]]
207 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
207 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
208 [[3], [5]]
208 [[3], [5]]
209
209
210 == No Snapshot cases ==
210 == No Snapshot cases ==
211 >>> revlog = _testrevlog(data)
211 >>> revlog = _testrevlog(data)
212
212
213 Cases where chunk is already small enough
213 Cases where chunk is already small enough
214 >>> list(_slicechunktosize(revlog, [0], 3))
214 >>> list(_slicechunktosize(revlog, [0], 3))
215 [[0]]
215 [[0]]
216 >>> list(_slicechunktosize(revlog, [6, 7], 3))
216 >>> list(_slicechunktosize(revlog, [6, 7], 3))
217 [[6, 7]]
217 [[6, 7]]
218 >>> list(_slicechunktosize(revlog, [0], None))
218 >>> list(_slicechunktosize(revlog, [0], None))
219 [[0]]
219 [[0]]
220 >>> list(_slicechunktosize(revlog, [6, 7], None))
220 >>> list(_slicechunktosize(revlog, [6, 7], None))
221 [[6, 7]]
221 [[6, 7]]
222
222
223 cases where we need actual slicing
223 cases where we need actual slicing
224 >>> list(_slicechunktosize(revlog, [0, 1], 3))
224 >>> list(_slicechunktosize(revlog, [0, 1], 3))
225 [[0], [1]]
225 [[0], [1]]
226 >>> list(_slicechunktosize(revlog, [1, 3], 3))
226 >>> list(_slicechunktosize(revlog, [1, 3], 3))
227 [[1], [3]]
227 [[1], [3]]
228 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
228 >>> list(_slicechunktosize(revlog, [1, 2, 3], 3))
229 [[1], [2, 3]]
229 [[1], [2, 3]]
230 >>> list(_slicechunktosize(revlog, [3, 5], 3))
230 >>> list(_slicechunktosize(revlog, [3, 5], 3))
231 [[3], [5]]
231 [[3], [5]]
232 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
232 >>> list(_slicechunktosize(revlog, [3, 4, 5], 3))
233 [[3], [4, 5]]
233 [[3], [4, 5]]
234 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
234 >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3))
235 [[5], [6, 7, 8]]
235 [[5], [6, 7, 8]]
236 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
236 >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3))
237 [[0], [1, 2], [3], [5], [6, 7, 8]]
237 [[0], [1, 2], [3], [5], [6, 7, 8]]
238
238
239 Case with too large individual chunk (must return valid chunk)
239 Case with too large individual chunk (must return valid chunk)
240 >>> list(_slicechunktosize(revlog, [0, 1], 2))
240 >>> list(_slicechunktosize(revlog, [0, 1], 2))
241 [[0], [1]]
241 [[0], [1]]
242 >>> list(_slicechunktosize(revlog, [1, 3], 1))
242 >>> list(_slicechunktosize(revlog, [1, 3], 1))
243 [[1], [3]]
243 [[1], [3]]
244 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
244 >>> list(_slicechunktosize(revlog, [3, 4, 5], 2))
245 [[3], [5]]
245 [[3], [5]]
246
246
247 == mixed case ==
247 == mixed case ==
248 >>> revlog = _testrevlog(data, snapshot=[0, 1, 2])
248 >>> revlog = _testrevlog(data, snapshot=[0, 1, 2])
249 >>> list(_slicechunktosize(revlog, list(range(9)), 5))
249 >>> list(_slicechunktosize(revlog, list(range(9)), 5))
250 [[0, 1], [2], [3, 4, 5], [6, 7, 8]]
250 [[0, 1], [2], [3, 4, 5], [6, 7, 8]]
251 """
251 """
252 assert targetsize is None or 0 <= targetsize
252 assert targetsize is None or 0 <= targetsize
253 startdata = revlog.start(revs[0])
253 startdata = revlog.start(revs[0])
254 enddata = revlog.end(revs[-1])
254 enddata = revlog.end(revs[-1])
255 fullspan = enddata - startdata
255 fullspan = enddata - startdata
256 if targetsize is None or fullspan <= targetsize:
256 if targetsize is None or fullspan <= targetsize:
257 yield revs
257 yield revs
258 return
258 return
259
259
260 startrevidx = 0
260 startrevidx = 0
261 endrevidx = 1
261 endrevidx = 1
262 iterrevs = enumerate(revs)
262 iterrevs = enumerate(revs)
263 next(iterrevs) # skip first rev.
263 next(iterrevs) # skip first rev.
264 # first step: get snapshots out of the way
264 # first step: get snapshots out of the way
265 for idx, r in iterrevs:
265 for idx, r in iterrevs:
266 span = revlog.end(r) - startdata
266 span = revlog.end(r) - startdata
267 snapshot = revlog.issnapshot(r)
267 snapshot = revlog.issnapshot(r)
268 if span <= targetsize and snapshot:
268 if span <= targetsize and snapshot:
269 endrevidx = idx + 1
269 endrevidx = idx + 1
270 else:
270 else:
271 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx)
271 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx)
272 if chunk:
272 if chunk:
273 yield chunk
273 yield chunk
274 startrevidx = idx
274 startrevidx = idx
275 startdata = revlog.start(r)
275 startdata = revlog.start(r)
276 endrevidx = idx + 1
276 endrevidx = idx + 1
277 if not snapshot:
277 if not snapshot:
278 break
278 break
279
279
280 # for the others, we use binary slicing to quickly converge toward valid
280 # for the others, we use binary slicing to quickly converge toward valid
281 # chunks (otherwise, we might end up looking for start/end of many
281 # chunks (otherwise, we might end up looking for start/end of many
282 # revisions). This logic is not looking for the perfect slicing point, it
282 # revisions). This logic is not looking for the perfect slicing point, it
283 # focuses on quickly converging toward valid chunks.
283 # focuses on quickly converging toward valid chunks.
284 nbitem = len(revs)
284 nbitem = len(revs)
285 while (enddata - startdata) > targetsize:
285 while (enddata - startdata) > targetsize:
286 endrevidx = nbitem
286 endrevidx = nbitem
287 if nbitem - startrevidx <= 1:
287 if nbitem - startrevidx <= 1:
288 break # protect against individual chunk larger than limit
288 break # protect against individual chunk larger than limit
289 localenddata = revlog.end(revs[endrevidx - 1])
289 localenddata = revlog.end(revs[endrevidx - 1])
290 span = localenddata - startdata
290 span = localenddata - startdata
291 while span > targetsize:
291 while span > targetsize:
292 if endrevidx - startrevidx <= 1:
292 if endrevidx - startrevidx <= 1:
293 break # protect against individual chunk larger than limit
293 break # protect against individual chunk larger than limit
294 endrevidx -= (endrevidx - startrevidx) // 2
294 endrevidx -= (endrevidx - startrevidx) // 2
295 localenddata = revlog.end(revs[endrevidx - 1])
295 localenddata = revlog.end(revs[endrevidx - 1])
296 span = localenddata - startdata
296 span = localenddata - startdata
297 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx)
297 chunk = _trimchunk(revlog, revs, startrevidx, endrevidx)
298 if chunk:
298 if chunk:
299 yield chunk
299 yield chunk
300 startrevidx = endrevidx
300 startrevidx = endrevidx
301 startdata = revlog.start(revs[startrevidx])
301 startdata = revlog.start(revs[startrevidx])
302
302
303 chunk = _trimchunk(revlog, revs, startrevidx)
303 chunk = _trimchunk(revlog, revs, startrevidx)
304 if chunk:
304 if chunk:
305 yield chunk
305 yield chunk
306
306
307
307
308 def _slicechunktodensity(revlog, revs, targetdensity=0.5, mingapsize=0):
308 def _slicechunktodensity(revlog, revs, targetdensity=0.5, mingapsize=0):
309 """slice revs to reduce the amount of unrelated data to be read from disk.
309 """slice revs to reduce the amount of unrelated data to be read from disk.
310
310
311 ``revs`` is sliced into groups that should be read in one time.
311 ``revs`` is sliced into groups that should be read in one time.
312 Assume that revs are sorted.
312 Assume that revs are sorted.
313
313
314 The initial chunk is sliced until the overall density (payload/chunks-span
314 The initial chunk is sliced until the overall density (payload/chunks-span
315 ratio) is above `targetdensity`. No gap smaller than `mingapsize` is
315 ratio) is above `targetdensity`. No gap smaller than `mingapsize` is
316 skipped.
316 skipped.
317
317
318 >>> revlog = _testrevlog([
318 >>> revlog = _testrevlog([
319 ... 5, #00 (5)
319 ... 5, #00 (5)
320 ... 10, #01 (5)
320 ... 10, #01 (5)
321 ... 12, #02 (2)
321 ... 12, #02 (2)
322 ... 12, #03 (empty)
322 ... 12, #03 (empty)
323 ... 27, #04 (15)
323 ... 27, #04 (15)
324 ... 31, #05 (4)
324 ... 31, #05 (4)
325 ... 31, #06 (empty)
325 ... 31, #06 (empty)
326 ... 42, #07 (11)
326 ... 42, #07 (11)
327 ... 47, #08 (5)
327 ... 47, #08 (5)
328 ... 47, #09 (empty)
328 ... 47, #09 (empty)
329 ... 48, #10 (1)
329 ... 48, #10 (1)
330 ... 51, #11 (3)
330 ... 51, #11 (3)
331 ... 74, #12 (23)
331 ... 74, #12 (23)
332 ... 85, #13 (11)
332 ... 85, #13 (11)
333 ... 86, #14 (1)
333 ... 86, #14 (1)
334 ... 91, #15 (5)
334 ... 91, #15 (5)
335 ... ])
335 ... ])
336
336
337 >>> list(_slicechunktodensity(revlog, list(range(16))))
337 >>> list(_slicechunktodensity(revlog, list(range(16))))
338 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
338 [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
339 >>> list(_slicechunktodensity(revlog, [0, 15]))
339 >>> list(_slicechunktodensity(revlog, [0, 15]))
340 [[0], [15]]
340 [[0], [15]]
341 >>> list(_slicechunktodensity(revlog, [0, 11, 15]))
341 >>> list(_slicechunktodensity(revlog, [0, 11, 15]))
342 [[0], [11], [15]]
342 [[0], [11], [15]]
343 >>> list(_slicechunktodensity(revlog, [0, 11, 13, 15]))
343 >>> list(_slicechunktodensity(revlog, [0, 11, 13, 15]))
344 [[0], [11, 13, 15]]
344 [[0], [11, 13, 15]]
345 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
345 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14]))
346 [[1, 2], [5, 8, 10, 11], [14]]
346 [[1, 2], [5, 8, 10, 11], [14]]
347 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
347 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
348 ... mingapsize=20))
348 ... mingapsize=20))
349 [[1, 2, 3, 5, 8, 10, 11], [14]]
349 [[1, 2, 3, 5, 8, 10, 11], [14]]
350 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
350 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
351 ... targetdensity=0.95))
351 ... targetdensity=0.95))
352 [[1, 2], [5], [8, 10, 11], [14]]
352 [[1, 2], [5], [8, 10, 11], [14]]
353 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
353 >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14],
354 ... targetdensity=0.95, mingapsize=12))
354 ... targetdensity=0.95, mingapsize=12))
355 [[1, 2], [5, 8, 10, 11], [14]]
355 [[1, 2], [5, 8, 10, 11], [14]]
356 """
356 """
357 start = revlog.start
357 start = revlog.start
358 length = revlog.length
358 length = revlog.length
359
359
360 if len(revs) <= 1:
360 if len(revs) <= 1:
361 yield revs
361 yield revs
362 return
362 return
363
363
364 deltachainspan = segmentspan(revlog, revs)
364 deltachainspan = segmentspan(revlog, revs)
365
365
366 if deltachainspan < mingapsize:
366 if deltachainspan < mingapsize:
367 yield revs
367 yield revs
368 return
368 return
369
369
370 readdata = deltachainspan
370 readdata = deltachainspan
371 chainpayload = sum(length(r) for r in revs)
371 chainpayload = sum(length(r) for r in revs)
372
372
373 if deltachainspan:
373 if deltachainspan:
374 density = chainpayload / float(deltachainspan)
374 density = chainpayload / float(deltachainspan)
375 else:
375 else:
376 density = 1.0
376 density = 1.0
377
377
378 if density >= targetdensity:
378 if density >= targetdensity:
379 yield revs
379 yield revs
380 return
380 return
381
381
382 # Store the gaps in a heap to have them sorted by decreasing size
382 # Store the gaps in a heap to have them sorted by decreasing size
383 gaps = []
383 gaps = []
384 prevend = None
384 prevend = None
385 for i, rev in enumerate(revs):
385 for i, rev in enumerate(revs):
386 revstart = start(rev)
386 revstart = start(rev)
387 revlen = length(rev)
387 revlen = length(rev)
388
388
389 # Skip empty revisions to form larger holes
389 # Skip empty revisions to form larger holes
390 if revlen == 0:
390 if revlen == 0:
391 continue
391 continue
392
392
393 if prevend is not None:
393 if prevend is not None:
394 gapsize = revstart - prevend
394 gapsize = revstart - prevend
395 # only consider holes that are large enough
395 # only consider holes that are large enough
396 if gapsize > mingapsize:
396 if gapsize > mingapsize:
397 gaps.append((gapsize, i))
397 gaps.append((gapsize, i))
398
398
399 prevend = revstart + revlen
399 prevend = revstart + revlen
400 # sort the gaps to pop them from largest to small
400 # sort the gaps to pop them from largest to small
401 gaps.sort()
401 gaps.sort()
402
402
403 # Collect the indices of the largest holes until the density is acceptable
403 # Collect the indices of the largest holes until the density is acceptable
404 selected = []
404 selected = []
405 while gaps and density < targetdensity:
405 while gaps and density < targetdensity:
406 gapsize, gapidx = gaps.pop()
406 gapsize, gapidx = gaps.pop()
407
407
408 selected.append(gapidx)
408 selected.append(gapidx)
409
409
410 # the gap sizes are stored as negatives to be sorted decreasingly
410 # the gap sizes are stored as negatives to be sorted decreasingly
411 # by the heap
411 # by the heap
412 readdata -= gapsize
412 readdata -= gapsize
413 if readdata > 0:
413 if readdata > 0:
414 density = chainpayload / float(readdata)
414 density = chainpayload / float(readdata)
415 else:
415 else:
416 density = 1.0
416 density = 1.0
417 selected.sort()
417 selected.sort()
418
418
419 # Cut the revs at collected indices
419 # Cut the revs at collected indices
420 previdx = 0
420 previdx = 0
421 for idx in selected:
421 for idx in selected:
422
422
423 chunk = _trimchunk(revlog, revs, previdx, idx)
423 chunk = _trimchunk(revlog, revs, previdx, idx)
424 if chunk:
424 if chunk:
425 yield chunk
425 yield chunk
426
426
427 previdx = idx
427 previdx = idx
428
428
429 chunk = _trimchunk(revlog, revs, previdx)
429 chunk = _trimchunk(revlog, revs, previdx)
430 if chunk:
430 if chunk:
431 yield chunk
431 yield chunk
432
432
433
433
434 def _trimchunk(revlog, revs, startidx, endidx=None):
434 def _trimchunk(revlog, revs, startidx, endidx=None):
435 """returns revs[startidx:endidx] without empty trailing revs
435 """returns revs[startidx:endidx] without empty trailing revs
436
436
437 Doctest Setup
437 Doctest Setup
438 >>> revlog = _testrevlog([
438 >>> revlog = _testrevlog([
439 ... 5, #0
439 ... 5, #0
440 ... 10, #1
440 ... 10, #1
441 ... 12, #2
441 ... 12, #2
442 ... 12, #3 (empty)
442 ... 12, #3 (empty)
443 ... 17, #4
443 ... 17, #4
444 ... 21, #5
444 ... 21, #5
445 ... 21, #6 (empty)
445 ... 21, #6 (empty)
446 ... ])
446 ... ])
447
447
448 Contiguous cases:
448 Contiguous cases:
449 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0)
449 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0)
450 [0, 1, 2, 3, 4, 5]
450 [0, 1, 2, 3, 4, 5]
451 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 5)
451 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 5)
452 [0, 1, 2, 3, 4]
452 [0, 1, 2, 3, 4]
453 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 4)
453 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 4)
454 [0, 1, 2]
454 [0, 1, 2]
455 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 2, 4)
455 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 2, 4)
456 [2]
456 [2]
457 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3)
457 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3)
458 [3, 4, 5]
458 [3, 4, 5]
459 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3, 5)
459 >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3, 5)
460 [3, 4]
460 [3, 4]
461
461
462 Discontiguous cases:
462 Discontiguous cases:
463 >>> _trimchunk(revlog, [1, 3, 5, 6], 0)
463 >>> _trimchunk(revlog, [1, 3, 5, 6], 0)
464 [1, 3, 5]
464 [1, 3, 5]
465 >>> _trimchunk(revlog, [1, 3, 5, 6], 0, 2)
465 >>> _trimchunk(revlog, [1, 3, 5, 6], 0, 2)
466 [1]
466 [1]
467 >>> _trimchunk(revlog, [1, 3, 5, 6], 1, 3)
467 >>> _trimchunk(revlog, [1, 3, 5, 6], 1, 3)
468 [3, 5]
468 [3, 5]
469 >>> _trimchunk(revlog, [1, 3, 5, 6], 1)
469 >>> _trimchunk(revlog, [1, 3, 5, 6], 1)
470 [3, 5]
470 [3, 5]
471 """
471 """
472 length = revlog.length
472 length = revlog.length
473
473
474 if endidx is None:
474 if endidx is None:
475 endidx = len(revs)
475 endidx = len(revs)
476
476
477 # If we have a non-emtpy delta candidate, there are nothing to trim
477 # If we have a non-emtpy delta candidate, there are nothing to trim
478 if revs[endidx - 1] < len(revlog):
478 if revs[endidx - 1] < len(revlog):
479 # Trim empty revs at the end, except the very first revision of a chain
479 # Trim empty revs at the end, except the very first revision of a chain
480 while (
480 while (
481 endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0
481 endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0
482 ):
482 ):
483 endidx -= 1
483 endidx -= 1
484
484
485 return revs[startidx:endidx]
485 return revs[startidx:endidx]
486
486
487
487
488 def segmentspan(revlog, revs):
488 def segmentspan(revlog, revs):
489 """Get the byte span of a segment of revisions
489 """Get the byte span of a segment of revisions
490
490
491 revs is a sorted array of revision numbers
491 revs is a sorted array of revision numbers
492
492
493 >>> revlog = _testrevlog([
493 >>> revlog = _testrevlog([
494 ... 5, #0
494 ... 5, #0
495 ... 10, #1
495 ... 10, #1
496 ... 12, #2
496 ... 12, #2
497 ... 12, #3 (empty)
497 ... 12, #3 (empty)
498 ... 17, #4
498 ... 17, #4
499 ... ])
499 ... ])
500
500
501 >>> segmentspan(revlog, [0, 1, 2, 3, 4])
501 >>> segmentspan(revlog, [0, 1, 2, 3, 4])
502 17
502 17
503 >>> segmentspan(revlog, [0, 4])
503 >>> segmentspan(revlog, [0, 4])
504 17
504 17
505 >>> segmentspan(revlog, [3, 4])
505 >>> segmentspan(revlog, [3, 4])
506 5
506 5
507 >>> segmentspan(revlog, [1, 2, 3,])
507 >>> segmentspan(revlog, [1, 2, 3,])
508 7
508 7
509 >>> segmentspan(revlog, [1, 3])
509 >>> segmentspan(revlog, [1, 3])
510 7
510 7
511 """
511 """
512 if not revs:
512 if not revs:
513 return 0
513 return 0
514 end = revlog.end(revs[-1])
514 end = revlog.end(revs[-1])
515 return end - revlog.start(revs[0])
515 return end - revlog.start(revs[0])
516
516
517
517
518 def _textfromdelta(fh, revlog, baserev, delta, p1, p2, flags, expectednode):
518 def _textfromdelta(fh, revlog, baserev, delta, p1, p2, flags, expectednode):
519 """build full text from a (base, delta) pair and other metadata"""
519 """build full text from a (base, delta) pair and other metadata"""
520 # special case deltas which replace entire base; no need to decode
520 # special case deltas which replace entire base; no need to decode
521 # base revision. this neatly avoids censored bases, which throw when
521 # base revision. this neatly avoids censored bases, which throw when
522 # they're decoded.
522 # they're decoded.
523 hlen = struct.calcsize(b">lll")
523 hlen = struct.calcsize(b">lll")
524 if delta[:hlen] == mdiff.replacediffheader(
524 if delta[:hlen] == mdiff.replacediffheader(
525 revlog.rawsize(baserev), len(delta) - hlen
525 revlog.rawsize(baserev), len(delta) - hlen
526 ):
526 ):
527 fulltext = delta[hlen:]
527 fulltext = delta[hlen:]
528 else:
528 else:
529 # deltabase is rawtext before changed by flag processors, which is
529 # deltabase is rawtext before changed by flag processors, which is
530 # equivalent to non-raw text
530 # equivalent to non-raw text
531 basetext = revlog.revision(baserev, _df=fh)
531 basetext = revlog.revision(baserev, _df=fh)
532 fulltext = mdiff.patch(basetext, delta)
532 fulltext = mdiff.patch(basetext, delta)
533
533
534 try:
534 try:
535 validatehash = flagutil.processflagsraw(revlog, fulltext, flags)
535 validatehash = flagutil.processflagsraw(revlog, fulltext, flags)
536 if validatehash:
536 if validatehash:
537 revlog.checkhash(fulltext, expectednode, p1=p1, p2=p2)
537 revlog.checkhash(fulltext, expectednode, p1=p1, p2=p2)
538 if flags & REVIDX_ISCENSORED:
538 if flags & REVIDX_ISCENSORED:
539 raise error.StorageError(
539 raise error.StorageError(
540 _(b'node %s is not censored') % expectednode
540 _(b'node %s is not censored') % expectednode
541 )
541 )
542 except error.CensoredNodeError:
542 except error.CensoredNodeError:
543 # must pass the censored index flag to add censored revisions
543 # must pass the censored index flag to add censored revisions
544 if not flags & REVIDX_ISCENSORED:
544 if not flags & REVIDX_ISCENSORED:
545 raise
545 raise
546 return fulltext
546 return fulltext
547
547
548
548
549 @attr.s(slots=True, frozen=True)
549 @attr.s(slots=True, frozen=True)
550 class _deltainfo:
550 class _deltainfo:
551 distance = attr.ib()
551 distance = attr.ib()
552 deltalen = attr.ib()
552 deltalen = attr.ib()
553 data = attr.ib()
553 data = attr.ib()
554 base = attr.ib()
554 base = attr.ib()
555 chainbase = attr.ib()
555 chainbase = attr.ib()
556 chainlen = attr.ib()
556 chainlen = attr.ib()
557 compresseddeltalen = attr.ib()
557 compresseddeltalen = attr.ib()
558 snapshotdepth = attr.ib()
558 snapshotdepth = attr.ib()
559
559
560
560
561 def drop_u_compression(delta):
561 def drop_u_compression(delta):
562 """turn into a "u" (no-compression) into no-compression without header
562 """turn into a "u" (no-compression) into no-compression without header
563
563
564 This is useful for revlog format that has better compression method.
564 This is useful for revlog format that has better compression method.
565 """
565 """
566 assert delta.data[0] == b'u', delta.data[0]
566 assert delta.data[0] == b'u', delta.data[0]
567 return _deltainfo(
567 return _deltainfo(
568 delta.distance,
568 delta.distance,
569 delta.deltalen - 1,
569 delta.deltalen - 1,
570 (b'', delta.data[1]),
570 (b'', delta.data[1]),
571 delta.base,
571 delta.base,
572 delta.chainbase,
572 delta.chainbase,
573 delta.chainlen,
573 delta.chainlen,
574 delta.compresseddeltalen,
574 delta.compresseddeltalen,
575 delta.snapshotdepth,
575 delta.snapshotdepth,
576 )
576 )
577
577
578
578
579 def isgooddeltainfo(revlog, deltainfo, revinfo):
579 def isgooddeltainfo(revlog, deltainfo, revinfo):
580 """Returns True if the given delta is good. Good means that it is within
580 """Returns True if the given delta is good. Good means that it is within
581 the disk span, disk size, and chain length bounds that we know to be
581 the disk span, disk size, and chain length bounds that we know to be
582 performant."""
582 performant."""
583 if deltainfo is None:
583 if deltainfo is None:
584 return False
584 return False
585
585
586 # - 'deltainfo.distance' is the distance from the base revision --
586 # - 'deltainfo.distance' is the distance from the base revision --
587 # bounding it limits the amount of I/O we need to do.
587 # bounding it limits the amount of I/O we need to do.
588 # - 'deltainfo.compresseddeltalen' is the sum of the total size of
588 # - 'deltainfo.compresseddeltalen' is the sum of the total size of
589 # deltas we need to apply -- bounding it limits the amount of CPU
589 # deltas we need to apply -- bounding it limits the amount of CPU
590 # we consume.
590 # we consume.
591
591
592 textlen = revinfo.textlen
592 textlen = revinfo.textlen
593 defaultmax = textlen * 4
593 defaultmax = textlen * 4
594 maxdist = revlog._maxdeltachainspan
594 maxdist = revlog._maxdeltachainspan
595 if not maxdist:
595 if not maxdist:
596 maxdist = deltainfo.distance # ensure the conditional pass
596 maxdist = deltainfo.distance # ensure the conditional pass
597 maxdist = max(maxdist, defaultmax)
597 maxdist = max(maxdist, defaultmax)
598
598
599 # Bad delta from read span:
599 # Bad delta from read span:
600 #
600 #
601 # If the span of data read is larger than the maximum allowed.
601 # If the span of data read is larger than the maximum allowed.
602 #
602 #
603 # In the sparse-revlog case, we rely on the associated "sparse reading"
603 # In the sparse-revlog case, we rely on the associated "sparse reading"
604 # to avoid issue related to the span of data. In theory, it would be
604 # to avoid issue related to the span of data. In theory, it would be
605 # possible to build pathological revlog where delta pattern would lead
605 # possible to build pathological revlog where delta pattern would lead
606 # to too many reads. However, they do not happen in practice at all. So
606 # to too many reads. However, they do not happen in practice at all. So
607 # we skip the span check entirely.
607 # we skip the span check entirely.
608 if not revlog._sparserevlog and maxdist < deltainfo.distance:
608 if not revlog._sparserevlog and maxdist < deltainfo.distance:
609 return False
609 return False
610
610
611 # Bad delta from new delta size:
611 # Bad delta from new delta size:
612 #
612 #
613 # If the delta size is larger than the target text, storing the
613 # If the delta size is larger than the target text, storing the
614 # delta will be inefficient.
614 # delta will be inefficient.
615 if textlen < deltainfo.deltalen:
615 if textlen < deltainfo.deltalen:
616 return False
616 return False
617
617
618 # Bad delta from cumulated payload size:
618 # Bad delta from cumulated payload size:
619 #
619 #
620 # If the sum of delta get larger than K * target text length.
620 # If the sum of delta get larger than K * target text length.
621 if textlen * LIMIT_DELTA2TEXT < deltainfo.compresseddeltalen:
621 if textlen * LIMIT_DELTA2TEXT < deltainfo.compresseddeltalen:
622 return False
622 return False
623
623
624 # Bad delta from chain length:
624 # Bad delta from chain length:
625 #
625 #
626 # If the number of delta in the chain gets too high.
626 # If the number of delta in the chain gets too high.
627 if revlog._maxchainlen and revlog._maxchainlen < deltainfo.chainlen:
627 if revlog._maxchainlen and revlog._maxchainlen < deltainfo.chainlen:
628 return False
628 return False
629
629
630 # bad delta from intermediate snapshot size limit
630 # bad delta from intermediate snapshot size limit
631 #
631 #
632 # If an intermediate snapshot size is higher than the limit. The
632 # If an intermediate snapshot size is higher than the limit. The
633 # limit exist to prevent endless chain of intermediate delta to be
633 # limit exist to prevent endless chain of intermediate delta to be
634 # created.
634 # created.
635 if (
635 if (
636 deltainfo.snapshotdepth is not None
636 deltainfo.snapshotdepth is not None
637 and (textlen >> deltainfo.snapshotdepth) < deltainfo.deltalen
637 and (textlen >> deltainfo.snapshotdepth) < deltainfo.deltalen
638 ):
638 ):
639 return False
639 return False
640
640
641 # bad delta if new intermediate snapshot is larger than the previous
641 # bad delta if new intermediate snapshot is larger than the previous
642 # snapshot
642 # snapshot
643 if (
643 if (
644 deltainfo.snapshotdepth
644 deltainfo.snapshotdepth
645 and revlog.length(deltainfo.base) < deltainfo.deltalen
645 and revlog.length(deltainfo.base) < deltainfo.deltalen
646 ):
646 ):
647 return False
647 return False
648
648
649 return True
649 return True
650
650
651
651
652 # If a revision's full text is that much bigger than a base candidate full
652 # If a revision's full text is that much bigger than a base candidate full
653 # text's, it is very unlikely that it will produce a valid delta. We no longer
653 # text's, it is very unlikely that it will produce a valid delta. We no longer
654 # consider these candidates.
654 # consider these candidates.
655 LIMIT_BASE2TEXT = 500
655 LIMIT_BASE2TEXT = 500
656
656
657
657
658 def _candidategroups(
658 def _candidategroups(
659 revlog,
659 revlog,
660 textlen,
660 textlen,
661 p1,
661 p1,
662 p2,
662 p2,
663 cachedelta,
663 cachedelta,
664 excluded_bases=None,
665 target_rev=None,
664 ):
666 ):
665 """Provides group of revision to be tested as delta base
667 """Provides group of revision to be tested as delta base
666
668
667 This top level function focus on emitting groups with unique and worthwhile
669 This top level function focus on emitting groups with unique and worthwhile
668 content. See _raw_candidate_groups for details about the group order.
670 content. See _raw_candidate_groups for details about the group order.
669 """
671 """
670 # should we try to build a delta?
672 # should we try to build a delta?
671 if not (len(revlog) and revlog._storedeltachains):
673 if not (len(revlog) and revlog._storedeltachains):
672 yield None
674 yield None
673 return
675 return
674
676
675 deltalength = revlog.length
677 deltalength = revlog.length
676 deltaparent = revlog.deltaparent
678 deltaparent = revlog.deltaparent
677 sparse = revlog._sparserevlog
679 sparse = revlog._sparserevlog
678 good = None
680 good = None
679
681
680 deltas_limit = textlen * LIMIT_DELTA2TEXT
682 deltas_limit = textlen * LIMIT_DELTA2TEXT
681
683
682 tested = {nullrev}
684 tested = {nullrev}
683 candidates = _refinedgroups(
685 candidates = _refinedgroups(
684 revlog,
686 revlog,
685 p1,
687 p1,
686 p2,
688 p2,
687 cachedelta,
689 cachedelta,
688 )
690 )
689 while True:
691 while True:
690 temptative = candidates.send(good)
692 temptative = candidates.send(good)
691 if temptative is None:
693 if temptative is None:
692 break
694 break
693 group = []
695 group = []
694 for rev in temptative:
696 for rev in temptative:
695 # skip over empty delta (no need to include them in a chain)
697 # skip over empty delta (no need to include them in a chain)
696 while revlog._generaldelta and not (
698 while revlog._generaldelta and not (
697 rev == nullrev or rev in tested or deltalength(rev)
699 rev == nullrev or rev in tested or deltalength(rev)
698 ):
700 ):
699 tested.add(rev)
701 tested.add(rev)
700 rev = deltaparent(rev)
702 rev = deltaparent(rev)
701 # no need to try a delta against nullrev, this will be done as a
703 # no need to try a delta against nullrev, this will be done as a
702 # last resort.
704 # last resort.
703 if rev == nullrev:
705 if rev == nullrev:
704 continue
706 continue
705 # filter out revision we tested already
707 # filter out revision we tested already
706 if rev in tested:
708 if rev in tested:
707 continue
709 continue
708 tested.add(rev)
710 tested.add(rev)
711 # an higher authority deamed the base unworthy (e.g. censored)
712 if excluded_bases is not None and rev in excluded_bases:
713 continue
714 # We are in some recomputation cases and that rev is too high in
715 # the revlog
716 if target_rev is not None and rev >= target_rev:
717 continue
709 # filter out delta base that will never produce good delta
718 # filter out delta base that will never produce good delta
710 if deltas_limit < revlog.length(rev):
719 if deltas_limit < revlog.length(rev):
711 continue
720 continue
712 if sparse and revlog.rawsize(rev) < (textlen // LIMIT_BASE2TEXT):
721 if sparse and revlog.rawsize(rev) < (textlen // LIMIT_BASE2TEXT):
713 continue
722 continue
714 # no delta for rawtext-changing revs (see "candelta" for why)
723 # no delta for rawtext-changing revs (see "candelta" for why)
715 if revlog.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS:
724 if revlog.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS:
716 continue
725 continue
717
726
718 # If we reach here, we are about to build and test a delta.
727 # If we reach here, we are about to build and test a delta.
719 # The delta building process will compute the chaininfo in all
728 # The delta building process will compute the chaininfo in all
720 # case, since that computation is cached, it is fine to access it
729 # case, since that computation is cached, it is fine to access it
721 # here too.
730 # here too.
722 chainlen, chainsize = revlog._chaininfo(rev)
731 chainlen, chainsize = revlog._chaininfo(rev)
723 # if chain will be too long, skip base
732 # if chain will be too long, skip base
724 if revlog._maxchainlen and chainlen >= revlog._maxchainlen:
733 if revlog._maxchainlen and chainlen >= revlog._maxchainlen:
725 continue
734 continue
726 # if chain already have too much data, skip base
735 # if chain already have too much data, skip base
727 if deltas_limit < chainsize:
736 if deltas_limit < chainsize:
728 continue
737 continue
729 if sparse and revlog.upperboundcomp is not None:
738 if sparse and revlog.upperboundcomp is not None:
730 maxcomp = revlog.upperboundcomp
739 maxcomp = revlog.upperboundcomp
731 basenotsnap = (p1, p2, nullrev)
740 basenotsnap = (p1, p2, nullrev)
732 if rev not in basenotsnap and revlog.issnapshot(rev):
741 if rev not in basenotsnap and revlog.issnapshot(rev):
733 snapshotdepth = revlog.snapshotdepth(rev)
742 snapshotdepth = revlog.snapshotdepth(rev)
734 # If text is significantly larger than the base, we can
743 # If text is significantly larger than the base, we can
735 # expect the resulting delta to be proportional to the size
744 # expect the resulting delta to be proportional to the size
736 # difference
745 # difference
737 revsize = revlog.rawsize(rev)
746 revsize = revlog.rawsize(rev)
738 rawsizedistance = max(textlen - revsize, 0)
747 rawsizedistance = max(textlen - revsize, 0)
739 # use an estimate of the compression upper bound.
748 # use an estimate of the compression upper bound.
740 lowestrealisticdeltalen = rawsizedistance // maxcomp
749 lowestrealisticdeltalen = rawsizedistance // maxcomp
741
750
742 # check the absolute constraint on the delta size
751 # check the absolute constraint on the delta size
743 snapshotlimit = textlen >> snapshotdepth
752 snapshotlimit = textlen >> snapshotdepth
744 if snapshotlimit < lowestrealisticdeltalen:
753 if snapshotlimit < lowestrealisticdeltalen:
745 # delta lower bound is larger than accepted upper bound
754 # delta lower bound is larger than accepted upper bound
746 continue
755 continue
747
756
748 # check the relative constraint on the delta size
757 # check the relative constraint on the delta size
749 revlength = revlog.length(rev)
758 revlength = revlog.length(rev)
750 if revlength < lowestrealisticdeltalen:
759 if revlength < lowestrealisticdeltalen:
751 # delta probable lower bound is larger than target base
760 # delta probable lower bound is larger than target base
752 continue
761 continue
753
762
754 group.append(rev)
763 group.append(rev)
755 if group:
764 if group:
756 # XXX: in the sparse revlog case, group can become large,
765 # XXX: in the sparse revlog case, group can become large,
757 # impacting performances. Some bounding or slicing mecanism
766 # impacting performances. Some bounding or slicing mecanism
758 # would help to reduce this impact.
767 # would help to reduce this impact.
759 good = yield tuple(group)
768 good = yield tuple(group)
760 yield None
769 yield None
761
770
762
771
763 def _findsnapshots(revlog, cache, start_rev):
772 def _findsnapshots(revlog, cache, start_rev):
764 """find snapshot from start_rev to tip"""
773 """find snapshot from start_rev to tip"""
765 if util.safehasattr(revlog.index, b'findsnapshots'):
774 if util.safehasattr(revlog.index, b'findsnapshots'):
766 revlog.index.findsnapshots(cache, start_rev)
775 revlog.index.findsnapshots(cache, start_rev)
767 else:
776 else:
768 deltaparent = revlog.deltaparent
777 deltaparent = revlog.deltaparent
769 issnapshot = revlog.issnapshot
778 issnapshot = revlog.issnapshot
770 for rev in revlog.revs(start_rev):
779 for rev in revlog.revs(start_rev):
771 if issnapshot(rev):
780 if issnapshot(rev):
772 cache[deltaparent(rev)].append(rev)
781 cache[deltaparent(rev)].append(rev)
773
782
774
783
775 def _refinedgroups(revlog, p1, p2, cachedelta):
784 def _refinedgroups(revlog, p1, p2, cachedelta):
776 good = None
785 good = None
777 # First we try to reuse a the delta contained in the bundle.
786 # First we try to reuse a the delta contained in the bundle.
778 # (or from the source revlog)
787 # (or from the source revlog)
779 #
788 #
780 # This logic only applies to general delta repositories and can be disabled
789 # This logic only applies to general delta repositories and can be disabled
781 # through configuration. Disabling reuse source delta is useful when
790 # through configuration. Disabling reuse source delta is useful when
782 # we want to make sure we recomputed "optimal" deltas.
791 # we want to make sure we recomputed "optimal" deltas.
783 debug_info = None
792 debug_info = None
784 if cachedelta and revlog._generaldelta and revlog._lazydeltabase:
793 if cachedelta and revlog._generaldelta and revlog._lazydeltabase:
785 # Assume what we received from the server is a good choice
794 # Assume what we received from the server is a good choice
786 # build delta will reuse the cache
795 # build delta will reuse the cache
787 if debug_info is not None:
796 if debug_info is not None:
788 debug_info['cached-delta.tested'] += 1
797 debug_info['cached-delta.tested'] += 1
789 good = yield (cachedelta[0],)
798 good = yield (cachedelta[0],)
790 if good is not None:
799 if good is not None:
791 if debug_info is not None:
800 if debug_info is not None:
792 debug_info['cached-delta.accepted'] += 1
801 debug_info['cached-delta.accepted'] += 1
793 yield None
802 yield None
794 return
803 return
795 # XXX cache me higher
804 # XXX cache me higher
796 snapshots = collections.defaultdict(list)
805 snapshots = collections.defaultdict(list)
797 groups = _rawgroups(
806 groups = _rawgroups(
798 revlog,
807 revlog,
799 p1,
808 p1,
800 p2,
809 p2,
801 cachedelta,
810 cachedelta,
802 snapshots,
811 snapshots,
803 )
812 )
804 for candidates in groups:
813 for candidates in groups:
805 good = yield candidates
814 good = yield candidates
806 if good is not None:
815 if good is not None:
807 break
816 break
808
817
809 # If sparse revlog is enabled, we can try to refine the available deltas
818 # If sparse revlog is enabled, we can try to refine the available deltas
810 if not revlog._sparserevlog:
819 if not revlog._sparserevlog:
811 yield None
820 yield None
812 return
821 return
813
822
814 # if we have a refinable value, try to refine it
823 # if we have a refinable value, try to refine it
815 if good is not None and good not in (p1, p2) and revlog.issnapshot(good):
824 if good is not None and good not in (p1, p2) and revlog.issnapshot(good):
816 # refine snapshot down
825 # refine snapshot down
817 previous = None
826 previous = None
818 while previous != good:
827 while previous != good:
819 previous = good
828 previous = good
820 base = revlog.deltaparent(good)
829 base = revlog.deltaparent(good)
821 if base == nullrev:
830 if base == nullrev:
822 break
831 break
823 good = yield (base,)
832 good = yield (base,)
824 # refine snapshot up
833 # refine snapshot up
825 if not snapshots:
834 if not snapshots:
826 _findsnapshots(revlog, snapshots, good + 1)
835 _findsnapshots(revlog, snapshots, good + 1)
827 previous = None
836 previous = None
828 while good != previous:
837 while good != previous:
829 previous = good
838 previous = good
830 children = tuple(sorted(c for c in snapshots[good]))
839 children = tuple(sorted(c for c in snapshots[good]))
831 good = yield children
840 good = yield children
832
841
833 if debug_info is not None:
842 if debug_info is not None:
834 if good is None:
843 if good is None:
835 debug_info['no-solution'] += 1
844 debug_info['no-solution'] += 1
836
845
837 yield None
846 yield None
838
847
839
848
840 def _rawgroups(revlog, p1, p2, cachedelta, snapshots=None):
849 def _rawgroups(revlog, p1, p2, cachedelta, snapshots=None):
841 """Provides group of revision to be tested as delta base
850 """Provides group of revision to be tested as delta base
842
851
843 This lower level function focus on emitting delta theorically interresting
852 This lower level function focus on emitting delta theorically interresting
844 without looking it any practical details.
853 without looking it any practical details.
845
854
846 The group order aims at providing fast or small candidates first.
855 The group order aims at providing fast or small candidates first.
847 """
856 """
848 gdelta = revlog._generaldelta
857 gdelta = revlog._generaldelta
849 # gate sparse behind general-delta because of issue6056
858 # gate sparse behind general-delta because of issue6056
850 sparse = gdelta and revlog._sparserevlog
859 sparse = gdelta and revlog._sparserevlog
851 curr = len(revlog)
860 curr = len(revlog)
852 prev = curr - 1
861 prev = curr - 1
853 deltachain = lambda rev: revlog._deltachain(rev)[0]
862 deltachain = lambda rev: revlog._deltachain(rev)[0]
854
863
855 if gdelta:
864 if gdelta:
856 # exclude already lazy tested base if any
865 # exclude already lazy tested base if any
857 parents = [p for p in (p1, p2) if p != nullrev]
866 parents = [p for p in (p1, p2) if p != nullrev]
858
867
859 if not revlog._deltabothparents and len(parents) == 2:
868 if not revlog._deltabothparents and len(parents) == 2:
860 parents.sort()
869 parents.sort()
861 # To minimize the chance of having to build a fulltext,
870 # To minimize the chance of having to build a fulltext,
862 # pick first whichever parent is closest to us (max rev)
871 # pick first whichever parent is closest to us (max rev)
863 yield (parents[1],)
872 yield (parents[1],)
864 # then the other one (min rev) if the first did not fit
873 # then the other one (min rev) if the first did not fit
865 yield (parents[0],)
874 yield (parents[0],)
866 elif len(parents) > 0:
875 elif len(parents) > 0:
867 # Test all parents (1 or 2), and keep the best candidate
876 # Test all parents (1 or 2), and keep the best candidate
868 yield parents
877 yield parents
869
878
870 if sparse and parents:
879 if sparse and parents:
871 if snapshots is None:
880 if snapshots is None:
872 # map: base-rev: snapshot-rev
881 # map: base-rev: snapshot-rev
873 snapshots = collections.defaultdict(list)
882 snapshots = collections.defaultdict(list)
874 # See if we can use an existing snapshot in the parent chains to use as
883 # See if we can use an existing snapshot in the parent chains to use as
875 # a base for a new intermediate-snapshot
884 # a base for a new intermediate-snapshot
876 #
885 #
877 # search for snapshot in parents delta chain
886 # search for snapshot in parents delta chain
878 # map: snapshot-level: snapshot-rev
887 # map: snapshot-level: snapshot-rev
879 parents_snaps = collections.defaultdict(set)
888 parents_snaps = collections.defaultdict(set)
880 candidate_chains = [deltachain(p) for p in parents]
889 candidate_chains = [deltachain(p) for p in parents]
881 for chain in candidate_chains:
890 for chain in candidate_chains:
882 for idx, s in enumerate(chain):
891 for idx, s in enumerate(chain):
883 if not revlog.issnapshot(s):
892 if not revlog.issnapshot(s):
884 break
893 break
885 parents_snaps[idx].add(s)
894 parents_snaps[idx].add(s)
886 snapfloor = min(parents_snaps[0]) + 1
895 snapfloor = min(parents_snaps[0]) + 1
887 _findsnapshots(revlog, snapshots, snapfloor)
896 _findsnapshots(revlog, snapshots, snapfloor)
888 # search for the highest "unrelated" revision
897 # search for the highest "unrelated" revision
889 #
898 #
890 # Adding snapshots used by "unrelated" revision increase the odd we
899 # Adding snapshots used by "unrelated" revision increase the odd we
891 # reuse an independant, yet better snapshot chain.
900 # reuse an independant, yet better snapshot chain.
892 #
901 #
893 # XXX instead of building a set of revisions, we could lazily enumerate
902 # XXX instead of building a set of revisions, we could lazily enumerate
894 # over the chains. That would be more efficient, however we stick to
903 # over the chains. That would be more efficient, however we stick to
895 # simple code for now.
904 # simple code for now.
896 all_revs = set()
905 all_revs = set()
897 for chain in candidate_chains:
906 for chain in candidate_chains:
898 all_revs.update(chain)
907 all_revs.update(chain)
899 other = None
908 other = None
900 for r in revlog.revs(prev, snapfloor):
909 for r in revlog.revs(prev, snapfloor):
901 if r not in all_revs:
910 if r not in all_revs:
902 other = r
911 other = r
903 break
912 break
904 if other is not None:
913 if other is not None:
905 # To avoid unfair competition, we won't use unrelated intermediate
914 # To avoid unfair competition, we won't use unrelated intermediate
906 # snapshot that are deeper than the ones from the parent delta
915 # snapshot that are deeper than the ones from the parent delta
907 # chain.
916 # chain.
908 max_depth = max(parents_snaps.keys())
917 max_depth = max(parents_snaps.keys())
909 chain = deltachain(other)
918 chain = deltachain(other)
910 for idx, s in enumerate(chain):
919 for idx, s in enumerate(chain):
911 if s < snapfloor:
920 if s < snapfloor:
912 continue
921 continue
913 if max_depth < idx:
922 if max_depth < idx:
914 break
923 break
915 if not revlog.issnapshot(s):
924 if not revlog.issnapshot(s):
916 break
925 break
917 parents_snaps[idx].add(s)
926 parents_snaps[idx].add(s)
918 # Test them as possible intermediate snapshot base
927 # Test them as possible intermediate snapshot base
919 # We test them from highest to lowest level. High level one are more
928 # We test them from highest to lowest level. High level one are more
920 # likely to result in small delta
929 # likely to result in small delta
921 floor = None
930 floor = None
922 for idx, snaps in sorted(parents_snaps.items(), reverse=True):
931 for idx, snaps in sorted(parents_snaps.items(), reverse=True):
923 siblings = set()
932 siblings = set()
924 for s in snaps:
933 for s in snaps:
925 siblings.update(snapshots[s])
934 siblings.update(snapshots[s])
926 # Before considering making a new intermediate snapshot, we check
935 # Before considering making a new intermediate snapshot, we check
927 # if an existing snapshot, children of base we consider, would be
936 # if an existing snapshot, children of base we consider, would be
928 # suitable.
937 # suitable.
929 #
938 #
930 # It give a change to reuse a delta chain "unrelated" to the
939 # It give a change to reuse a delta chain "unrelated" to the
931 # current revision instead of starting our own. Without such
940 # current revision instead of starting our own. Without such
932 # re-use, topological branches would keep reopening new chains.
941 # re-use, topological branches would keep reopening new chains.
933 # Creating more and more snapshot as the repository grow.
942 # Creating more and more snapshot as the repository grow.
934
943
935 if floor is not None:
944 if floor is not None:
936 # We only do this for siblings created after the one in our
945 # We only do this for siblings created after the one in our
937 # parent's delta chain. Those created before has less chances
946 # parent's delta chain. Those created before has less chances
938 # to be valid base since our ancestors had to create a new
947 # to be valid base since our ancestors had to create a new
939 # snapshot.
948 # snapshot.
940 siblings = [r for r in siblings if floor < r]
949 siblings = [r for r in siblings if floor < r]
941 yield tuple(sorted(siblings))
950 yield tuple(sorted(siblings))
942 # then test the base from our parent's delta chain.
951 # then test the base from our parent's delta chain.
943 yield tuple(sorted(snaps))
952 yield tuple(sorted(snaps))
944 floor = min(snaps)
953 floor = min(snaps)
945 # No suitable base found in the parent chain, search if any full
954 # No suitable base found in the parent chain, search if any full
946 # snapshots emitted since parent's base would be a suitable base for an
955 # snapshots emitted since parent's base would be a suitable base for an
947 # intermediate snapshot.
956 # intermediate snapshot.
948 #
957 #
949 # It give a chance to reuse a delta chain unrelated to the current
958 # It give a chance to reuse a delta chain unrelated to the current
950 # revisions instead of starting our own. Without such re-use,
959 # revisions instead of starting our own. Without such re-use,
951 # topological branches would keep reopening new full chains. Creating
960 # topological branches would keep reopening new full chains. Creating
952 # more and more snapshot as the repository grow.
961 # more and more snapshot as the repository grow.
953 yield tuple(snapshots[nullrev])
962 yield tuple(snapshots[nullrev])
954
963
955 if not sparse:
964 if not sparse:
956 # other approach failed try against prev to hopefully save us a
965 # other approach failed try against prev to hopefully save us a
957 # fulltext.
966 # fulltext.
958 yield (prev,)
967 yield (prev,)
959
968
960
969
961 class deltacomputer:
970 class deltacomputer:
962 def __init__(
971 def __init__(
963 self,
972 self,
964 revlog,
973 revlog,
965 write_debug=None,
974 write_debug=None,
966 debug_search=False,
975 debug_search=False,
967 debug_info=None,
976 debug_info=None,
968 ):
977 ):
969 self.revlog = revlog
978 self.revlog = revlog
970 self._write_debug = write_debug
979 self._write_debug = write_debug
971 self._debug_search = debug_search
980 self._debug_search = debug_search
972 self._debug_info = debug_info
981 self._debug_info = debug_info
973
982
974 def buildtext(self, revinfo, fh):
983 def buildtext(self, revinfo, fh):
975 """Builds a fulltext version of a revision
984 """Builds a fulltext version of a revision
976
985
977 revinfo: revisioninfo instance that contains all needed info
986 revinfo: revisioninfo instance that contains all needed info
978 fh: file handle to either the .i or the .d revlog file,
987 fh: file handle to either the .i or the .d revlog file,
979 depending on whether it is inlined or not
988 depending on whether it is inlined or not
980 """
989 """
981 btext = revinfo.btext
990 btext = revinfo.btext
982 if btext[0] is not None:
991 if btext[0] is not None:
983 return btext[0]
992 return btext[0]
984
993
985 revlog = self.revlog
994 revlog = self.revlog
986 cachedelta = revinfo.cachedelta
995 cachedelta = revinfo.cachedelta
987 baserev = cachedelta[0]
996 baserev = cachedelta[0]
988 delta = cachedelta[1]
997 delta = cachedelta[1]
989
998
990 fulltext = btext[0] = _textfromdelta(
999 fulltext = btext[0] = _textfromdelta(
991 fh,
1000 fh,
992 revlog,
1001 revlog,
993 baserev,
1002 baserev,
994 delta,
1003 delta,
995 revinfo.p1,
1004 revinfo.p1,
996 revinfo.p2,
1005 revinfo.p2,
997 revinfo.flags,
1006 revinfo.flags,
998 revinfo.node,
1007 revinfo.node,
999 )
1008 )
1000 return fulltext
1009 return fulltext
1001
1010
1002 def _builddeltadiff(self, base, revinfo, fh):
1011 def _builddeltadiff(self, base, revinfo, fh):
1003 revlog = self.revlog
1012 revlog = self.revlog
1004 t = self.buildtext(revinfo, fh)
1013 t = self.buildtext(revinfo, fh)
1005 if revlog.iscensored(base):
1014 if revlog.iscensored(base):
1006 # deltas based on a censored revision must replace the
1015 # deltas based on a censored revision must replace the
1007 # full content in one patch, so delta works everywhere
1016 # full content in one patch, so delta works everywhere
1008 header = mdiff.replacediffheader(revlog.rawsize(base), len(t))
1017 header = mdiff.replacediffheader(revlog.rawsize(base), len(t))
1009 delta = header + t
1018 delta = header + t
1010 else:
1019 else:
1011 ptext = revlog.rawdata(base, _df=fh)
1020 ptext = revlog.rawdata(base, _df=fh)
1012 delta = mdiff.textdiff(ptext, t)
1021 delta = mdiff.textdiff(ptext, t)
1013
1022
1014 return delta
1023 return delta
1015
1024
1016 def _builddeltainfo(self, revinfo, base, fh):
1025 def _builddeltainfo(self, revinfo, base, fh):
1017 # can we use the cached delta?
1026 # can we use the cached delta?
1018 revlog = self.revlog
1027 revlog = self.revlog
1019 debug_search = self._write_debug is not None and self._debug_search
1028 debug_search = self._write_debug is not None and self._debug_search
1020 chainbase = revlog.chainbase(base)
1029 chainbase = revlog.chainbase(base)
1021 if revlog._generaldelta:
1030 if revlog._generaldelta:
1022 deltabase = base
1031 deltabase = base
1023 else:
1032 else:
1024 deltabase = chainbase
1033 deltabase = chainbase
1025 snapshotdepth = None
1034 snapshotdepth = None
1026 if revlog._sparserevlog and deltabase == nullrev:
1035 if revlog._sparserevlog and deltabase == nullrev:
1027 snapshotdepth = 0
1036 snapshotdepth = 0
1028 elif revlog._sparserevlog and revlog.issnapshot(deltabase):
1037 elif revlog._sparserevlog and revlog.issnapshot(deltabase):
1029 # A delta chain should always be one full snapshot,
1038 # A delta chain should always be one full snapshot,
1030 # zero or more semi-snapshots, and zero or more deltas
1039 # zero or more semi-snapshots, and zero or more deltas
1031 p1, p2 = revlog.rev(revinfo.p1), revlog.rev(revinfo.p2)
1040 p1, p2 = revlog.rev(revinfo.p1), revlog.rev(revinfo.p2)
1032 if deltabase not in (p1, p2) and revlog.issnapshot(deltabase):
1041 if deltabase not in (p1, p2) and revlog.issnapshot(deltabase):
1033 snapshotdepth = len(revlog._deltachain(deltabase)[0])
1042 snapshotdepth = len(revlog._deltachain(deltabase)[0])
1034 delta = None
1043 delta = None
1035 if revinfo.cachedelta:
1044 if revinfo.cachedelta:
1036 cachebase, cachediff = revinfo.cachedelta
1045 cachebase, cachediff = revinfo.cachedelta
1037 # check if the diff still apply
1046 # check if the diff still apply
1038 currentbase = cachebase
1047 currentbase = cachebase
1039 while (
1048 while (
1040 currentbase != nullrev
1049 currentbase != nullrev
1041 and currentbase != base
1050 and currentbase != base
1042 and self.revlog.length(currentbase) == 0
1051 and self.revlog.length(currentbase) == 0
1043 ):
1052 ):
1044 currentbase = self.revlog.deltaparent(currentbase)
1053 currentbase = self.revlog.deltaparent(currentbase)
1045 if self.revlog._lazydelta and currentbase == base:
1054 if self.revlog._lazydelta and currentbase == base:
1046 delta = revinfo.cachedelta[1]
1055 delta = revinfo.cachedelta[1]
1047 if delta is None:
1056 if delta is None:
1048 delta = self._builddeltadiff(base, revinfo, fh)
1057 delta = self._builddeltadiff(base, revinfo, fh)
1049 if debug_search:
1058 if debug_search:
1050 msg = b"DBG-DELTAS-SEARCH: uncompressed-delta-size=%d\n"
1059 msg = b"DBG-DELTAS-SEARCH: uncompressed-delta-size=%d\n"
1051 msg %= len(delta)
1060 msg %= len(delta)
1052 self._write_debug(msg)
1061 self._write_debug(msg)
1053 # snapshotdept need to be neither None nor 0 level snapshot
1062 # snapshotdept need to be neither None nor 0 level snapshot
1054 if revlog.upperboundcomp is not None and snapshotdepth:
1063 if revlog.upperboundcomp is not None and snapshotdepth:
1055 lowestrealisticdeltalen = len(delta) // revlog.upperboundcomp
1064 lowestrealisticdeltalen = len(delta) // revlog.upperboundcomp
1056 snapshotlimit = revinfo.textlen >> snapshotdepth
1065 snapshotlimit = revinfo.textlen >> snapshotdepth
1057 if debug_search:
1066 if debug_search:
1058 msg = b"DBG-DELTAS-SEARCH: projected-lower-size=%d\n"
1067 msg = b"DBG-DELTAS-SEARCH: projected-lower-size=%d\n"
1059 msg %= lowestrealisticdeltalen
1068 msg %= lowestrealisticdeltalen
1060 self._write_debug(msg)
1069 self._write_debug(msg)
1061 if snapshotlimit < lowestrealisticdeltalen:
1070 if snapshotlimit < lowestrealisticdeltalen:
1062 if debug_search:
1071 if debug_search:
1063 msg = b"DBG-DELTAS-SEARCH: DISCARDED (snapshot limit)\n"
1072 msg = b"DBG-DELTAS-SEARCH: DISCARDED (snapshot limit)\n"
1064 self._write_debug(msg)
1073 self._write_debug(msg)
1065 return None
1074 return None
1066 if revlog.length(base) < lowestrealisticdeltalen:
1075 if revlog.length(base) < lowestrealisticdeltalen:
1067 if debug_search:
1076 if debug_search:
1068 msg = b"DBG-DELTAS-SEARCH: DISCARDED (prev size)\n"
1077 msg = b"DBG-DELTAS-SEARCH: DISCARDED (prev size)\n"
1069 self._write_debug(msg)
1078 self._write_debug(msg)
1070 return None
1079 return None
1071 header, data = revlog.compress(delta)
1080 header, data = revlog.compress(delta)
1072 deltalen = len(header) + len(data)
1081 deltalen = len(header) + len(data)
1073 offset = revlog.end(len(revlog) - 1)
1082 offset = revlog.end(len(revlog) - 1)
1074 dist = deltalen + offset - revlog.start(chainbase)
1083 dist = deltalen + offset - revlog.start(chainbase)
1075 chainlen, compresseddeltalen = revlog._chaininfo(base)
1084 chainlen, compresseddeltalen = revlog._chaininfo(base)
1076 chainlen += 1
1085 chainlen += 1
1077 compresseddeltalen += deltalen
1086 compresseddeltalen += deltalen
1078
1087
1079 return _deltainfo(
1088 return _deltainfo(
1080 dist,
1089 dist,
1081 deltalen,
1090 deltalen,
1082 (header, data),
1091 (header, data),
1083 deltabase,
1092 deltabase,
1084 chainbase,
1093 chainbase,
1085 chainlen,
1094 chainlen,
1086 compresseddeltalen,
1095 compresseddeltalen,
1087 snapshotdepth,
1096 snapshotdepth,
1088 )
1097 )
1089
1098
1090 def _fullsnapshotinfo(self, fh, revinfo, curr):
1099 def _fullsnapshotinfo(self, fh, revinfo, curr):
1091 rawtext = self.buildtext(revinfo, fh)
1100 rawtext = self.buildtext(revinfo, fh)
1092 data = self.revlog.compress(rawtext)
1101 data = self.revlog.compress(rawtext)
1093 compresseddeltalen = deltalen = dist = len(data[1]) + len(data[0])
1102 compresseddeltalen = deltalen = dist = len(data[1]) + len(data[0])
1094 deltabase = chainbase = curr
1103 deltabase = chainbase = curr
1095 snapshotdepth = 0
1104 snapshotdepth = 0
1096 chainlen = 1
1105 chainlen = 1
1097
1106
1098 return _deltainfo(
1107 return _deltainfo(
1099 dist,
1108 dist,
1100 deltalen,
1109 deltalen,
1101 data,
1110 data,
1102 deltabase,
1111 deltabase,
1103 chainbase,
1112 chainbase,
1104 chainlen,
1113 chainlen,
1105 compresseddeltalen,
1114 compresseddeltalen,
1106 snapshotdepth,
1115 snapshotdepth,
1107 )
1116 )
1108
1117
1109 def finddeltainfo(self, revinfo, fh, excluded_bases=None, target_rev=None):
1118 def finddeltainfo(self, revinfo, fh, excluded_bases=None, target_rev=None):
1110 """Find an acceptable delta against a candidate revision
1119 """Find an acceptable delta against a candidate revision
1111
1120
1112 revinfo: information about the revision (instance of _revisioninfo)
1121 revinfo: information about the revision (instance of _revisioninfo)
1113 fh: file handle to either the .i or the .d revlog file,
1122 fh: file handle to either the .i or the .d revlog file,
1114 depending on whether it is inlined or not
1123 depending on whether it is inlined or not
1115
1124
1116 Returns the first acceptable candidate revision, as ordered by
1125 Returns the first acceptable candidate revision, as ordered by
1117 _candidategroups
1126 _candidategroups
1118
1127
1119 If no suitable deltabase is found, we return delta info for a full
1128 If no suitable deltabase is found, we return delta info for a full
1120 snapshot.
1129 snapshot.
1121
1130
1122 `excluded_bases` is an optional set of revision that cannot be used as
1131 `excluded_bases` is an optional set of revision that cannot be used as
1123 a delta base. Use this to recompute delta suitable in censor or strip
1132 a delta base. Use this to recompute delta suitable in censor or strip
1124 context.
1133 context.
1125 """
1134 """
1126 if target_rev is None:
1135 if target_rev is None:
1127 target_rev = len(self.revlog)
1136 target_rev = len(self.revlog)
1128
1137
1129 if not revinfo.textlen:
1138 if not revinfo.textlen:
1130 return self._fullsnapshotinfo(fh, revinfo, target_rev)
1139 return self._fullsnapshotinfo(fh, revinfo, target_rev)
1131
1140
1132 if excluded_bases is None:
1141 if excluded_bases is None:
1133 excluded_bases = set()
1142 excluded_bases = set()
1134
1143
1135 # no delta for flag processor revision (see "candelta" for why)
1144 # no delta for flag processor revision (see "candelta" for why)
1136 # not calling candelta since only one revision needs test, also to
1145 # not calling candelta since only one revision needs test, also to
1137 # avoid overhead fetching flags again.
1146 # avoid overhead fetching flags again.
1138 if revinfo.flags & REVIDX_RAWTEXT_CHANGING_FLAGS:
1147 if revinfo.flags & REVIDX_RAWTEXT_CHANGING_FLAGS:
1139 return self._fullsnapshotinfo(fh, revinfo, target_rev)
1148 return self._fullsnapshotinfo(fh, revinfo, target_rev)
1140
1149
1141 gather_debug = (
1150 gather_debug = (
1142 self._write_debug is not None or self._debug_info is not None
1151 self._write_debug is not None or self._debug_info is not None
1143 )
1152 )
1144 debug_search = self._write_debug is not None and self._debug_search
1153 debug_search = self._write_debug is not None and self._debug_search
1145
1154
1146 if gather_debug:
1155 if gather_debug:
1147 start = util.timer()
1156 start = util.timer()
1148
1157
1149 # count the number of different delta we tried (for debug purpose)
1158 # count the number of different delta we tried (for debug purpose)
1150 dbg_try_count = 0
1159 dbg_try_count = 0
1151 # count the number of "search round" we did. (for debug purpose)
1160 # count the number of "search round" we did. (for debug purpose)
1152 dbg_try_rounds = 0
1161 dbg_try_rounds = 0
1153 dbg_type = b'unknown'
1162 dbg_type = b'unknown'
1154
1163
1155 cachedelta = revinfo.cachedelta
1164 cachedelta = revinfo.cachedelta
1156 p1 = revinfo.p1
1165 p1 = revinfo.p1
1157 p2 = revinfo.p2
1166 p2 = revinfo.p2
1158 revlog = self.revlog
1167 revlog = self.revlog
1159
1168
1160 deltainfo = None
1169 deltainfo = None
1161 p1r, p2r = revlog.rev(p1), revlog.rev(p2)
1170 p1r, p2r = revlog.rev(p1), revlog.rev(p2)
1162
1171
1163 if gather_debug:
1172 if gather_debug:
1164 if p1r != nullrev:
1173 if p1r != nullrev:
1165 p1_chain_len = revlog._chaininfo(p1r)[0]
1174 p1_chain_len = revlog._chaininfo(p1r)[0]
1166 else:
1175 else:
1167 p1_chain_len = -1
1176 p1_chain_len = -1
1168 if p2r != nullrev:
1177 if p2r != nullrev:
1169 p2_chain_len = revlog._chaininfo(p2r)[0]
1178 p2_chain_len = revlog._chaininfo(p2r)[0]
1170 else:
1179 else:
1171 p2_chain_len = -1
1180 p2_chain_len = -1
1172 if debug_search:
1181 if debug_search:
1173 msg = b"DBG-DELTAS-SEARCH: SEARCH rev=%d\n"
1182 msg = b"DBG-DELTAS-SEARCH: SEARCH rev=%d\n"
1174 msg %= target_rev
1183 msg %= target_rev
1175 self._write_debug(msg)
1184 self._write_debug(msg)
1176
1185
1177 groups = _candidategroups(
1186 groups = _candidategroups(
1178 self.revlog,
1187 self.revlog,
1179 revinfo.textlen,
1188 revinfo.textlen,
1180 p1r,
1189 p1r,
1181 p2r,
1190 p2r,
1182 cachedelta,
1191 cachedelta,
1192 excluded_bases,
1193 target_rev,
1183 )
1194 )
1184 candidaterevs = next(groups)
1195 candidaterevs = next(groups)
1185 while candidaterevs is not None:
1196 while candidaterevs is not None:
1186 dbg_try_rounds += 1
1197 dbg_try_rounds += 1
1187 if debug_search:
1198 if debug_search:
1188 prev = None
1199 prev = None
1189 if deltainfo is not None:
1200 if deltainfo is not None:
1190 prev = deltainfo.base
1201 prev = deltainfo.base
1191
1202
1192 if (
1203 if (
1193 cachedelta is not None
1204 cachedelta is not None
1194 and len(candidaterevs) == 1
1205 and len(candidaterevs) == 1
1195 and cachedelta[0] in candidaterevs
1206 and cachedelta[0] in candidaterevs
1196 ):
1207 ):
1197 round_type = b"cached-delta"
1208 round_type = b"cached-delta"
1198 elif p1 in candidaterevs or p2 in candidaterevs:
1209 elif p1 in candidaterevs or p2 in candidaterevs:
1199 round_type = b"parents"
1210 round_type = b"parents"
1200 elif prev is not None and all(c < prev for c in candidaterevs):
1211 elif prev is not None and all(c < prev for c in candidaterevs):
1201 round_type = b"refine-down"
1212 round_type = b"refine-down"
1202 elif prev is not None and all(c > prev for c in candidaterevs):
1213 elif prev is not None and all(c > prev for c in candidaterevs):
1203 round_type = b"refine-up"
1214 round_type = b"refine-up"
1204 else:
1215 else:
1205 round_type = b"search-down"
1216 round_type = b"search-down"
1206 msg = b"DBG-DELTAS-SEARCH: ROUND #%d - %d candidates - %s\n"
1217 msg = b"DBG-DELTAS-SEARCH: ROUND #%d - %d candidates - %s\n"
1207 msg %= (dbg_try_rounds, len(candidaterevs), round_type)
1218 msg %= (dbg_try_rounds, len(candidaterevs), round_type)
1208 self._write_debug(msg)
1219 self._write_debug(msg)
1209 nominateddeltas = []
1220 nominateddeltas = []
1210 if deltainfo is not None:
1221 if deltainfo is not None:
1211 if debug_search:
1222 if debug_search:
1212 msg = (
1223 msg = (
1213 b"DBG-DELTAS-SEARCH: CONTENDER: rev=%d - length=%d\n"
1224 b"DBG-DELTAS-SEARCH: CONTENDER: rev=%d - length=%d\n"
1214 )
1225 )
1215 msg %= (deltainfo.base, deltainfo.deltalen)
1226 msg %= (deltainfo.base, deltainfo.deltalen)
1216 self._write_debug(msg)
1227 self._write_debug(msg)
1217 # if we already found a good delta,
1228 # if we already found a good delta,
1218 # challenge it against refined candidates
1229 # challenge it against refined candidates
1219 nominateddeltas.append(deltainfo)
1230 nominateddeltas.append(deltainfo)
1220 for candidaterev in candidaterevs:
1231 for candidaterev in candidaterevs:
1221 if debug_search:
1232 if debug_search:
1222 msg = b"DBG-DELTAS-SEARCH: CANDIDATE: rev=%d\n"
1233 msg = b"DBG-DELTAS-SEARCH: CANDIDATE: rev=%d\n"
1223 msg %= candidaterev
1234 msg %= candidaterev
1224 self._write_debug(msg)
1235 self._write_debug(msg)
1225 candidate_type = None
1236 candidate_type = None
1226 if candidaterev == p1:
1237 if candidaterev == p1:
1227 candidate_type = b"p1"
1238 candidate_type = b"p1"
1228 elif candidaterev == p2:
1239 elif candidaterev == p2:
1229 candidate_type = b"p2"
1240 candidate_type = b"p2"
1230 elif self.revlog.issnapshot(candidaterev):
1241 elif self.revlog.issnapshot(candidaterev):
1231 candidate_type = b"snapshot-%d"
1242 candidate_type = b"snapshot-%d"
1232 candidate_type %= self.revlog.snapshotdepth(
1243 candidate_type %= self.revlog.snapshotdepth(
1233 candidaterev
1244 candidaterev
1234 )
1245 )
1235
1246
1236 if candidate_type is not None:
1247 if candidate_type is not None:
1237 msg = b"DBG-DELTAS-SEARCH: type=%s\n"
1248 msg = b"DBG-DELTAS-SEARCH: type=%s\n"
1238 msg %= candidate_type
1249 msg %= candidate_type
1239 self._write_debug(msg)
1250 self._write_debug(msg)
1240 msg = b"DBG-DELTAS-SEARCH: size=%d\n"
1251 msg = b"DBG-DELTAS-SEARCH: size=%d\n"
1241 msg %= self.revlog.length(candidaterev)
1252 msg %= self.revlog.length(candidaterev)
1242 self._write_debug(msg)
1253 self._write_debug(msg)
1243 msg = b"DBG-DELTAS-SEARCH: base=%d\n"
1254 msg = b"DBG-DELTAS-SEARCH: base=%d\n"
1244 msg %= self.revlog.deltaparent(candidaterev)
1255 msg %= self.revlog.deltaparent(candidaterev)
1245 self._write_debug(msg)
1256 self._write_debug(msg)
1246 if candidaterev in excluded_bases:
1257
1247 if debug_search:
1248 msg = b"DBG-DELTAS-SEARCH: EXCLUDED\n"
1249 self._write_debug(msg)
1250 continue
1251 if candidaterev >= target_rev:
1252 if debug_search:
1253 msg = b"DBG-DELTAS-SEARCH: TOO-HIGH\n"
1254 self._write_debug(msg)
1255 continue
1256 dbg_try_count += 1
1258 dbg_try_count += 1
1257
1259
1258 if debug_search:
1260 if debug_search:
1259 delta_start = util.timer()
1261 delta_start = util.timer()
1260 candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh)
1262 candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh)
1261 if debug_search:
1263 if debug_search:
1262 delta_end = util.timer()
1264 delta_end = util.timer()
1263 msg = b"DBG-DELTAS-SEARCH: delta-search-time=%f\n"
1265 msg = b"DBG-DELTAS-SEARCH: delta-search-time=%f\n"
1264 msg %= delta_end - delta_start
1266 msg %= delta_end - delta_start
1265 self._write_debug(msg)
1267 self._write_debug(msg)
1266 if candidatedelta is not None:
1268 if candidatedelta is not None:
1267 if isgooddeltainfo(self.revlog, candidatedelta, revinfo):
1269 if isgooddeltainfo(self.revlog, candidatedelta, revinfo):
1268 if debug_search:
1270 if debug_search:
1269 msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (GOOD)\n"
1271 msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (GOOD)\n"
1270 msg %= candidatedelta.deltalen
1272 msg %= candidatedelta.deltalen
1271 self._write_debug(msg)
1273 self._write_debug(msg)
1272 nominateddeltas.append(candidatedelta)
1274 nominateddeltas.append(candidatedelta)
1273 elif debug_search:
1275 elif debug_search:
1274 msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (BAD)\n"
1276 msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (BAD)\n"
1275 msg %= candidatedelta.deltalen
1277 msg %= candidatedelta.deltalen
1276 self._write_debug(msg)
1278 self._write_debug(msg)
1277 elif debug_search:
1279 elif debug_search:
1278 msg = b"DBG-DELTAS-SEARCH: NO-DELTA\n"
1280 msg = b"DBG-DELTAS-SEARCH: NO-DELTA\n"
1279 self._write_debug(msg)
1281 self._write_debug(msg)
1280 if nominateddeltas:
1282 if nominateddeltas:
1281 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
1283 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
1282 if deltainfo is not None:
1284 if deltainfo is not None:
1283 candidaterevs = groups.send(deltainfo.base)
1285 candidaterevs = groups.send(deltainfo.base)
1284 else:
1286 else:
1285 candidaterevs = next(groups)
1287 candidaterevs = next(groups)
1286
1288
1287 if deltainfo is None:
1289 if deltainfo is None:
1288 dbg_type = b"full"
1290 dbg_type = b"full"
1289 deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev)
1291 deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev)
1290 elif deltainfo.snapshotdepth: # pytype: disable=attribute-error
1292 elif deltainfo.snapshotdepth: # pytype: disable=attribute-error
1291 dbg_type = b"snapshot"
1293 dbg_type = b"snapshot"
1292 else:
1294 else:
1293 dbg_type = b"delta"
1295 dbg_type = b"delta"
1294
1296
1295 if gather_debug:
1297 if gather_debug:
1296 end = util.timer()
1298 end = util.timer()
1297 used_cached = (
1299 used_cached = (
1298 cachedelta is not None
1300 cachedelta is not None
1299 and dbg_try_rounds == 1
1301 and dbg_try_rounds == 1
1300 and dbg_try_count == 1
1302 and dbg_try_count == 1
1301 and deltainfo.base == cachedelta[0]
1303 and deltainfo.base == cachedelta[0]
1302 )
1304 )
1303 dbg = {
1305 dbg = {
1304 'duration': end - start,
1306 'duration': end - start,
1305 'revision': target_rev,
1307 'revision': target_rev,
1306 'delta-base': deltainfo.base, # pytype: disable=attribute-error
1308 'delta-base': deltainfo.base, # pytype: disable=attribute-error
1307 'search_round_count': dbg_try_rounds,
1309 'search_round_count': dbg_try_rounds,
1308 'using-cached-base': used_cached,
1310 'using-cached-base': used_cached,
1309 'delta_try_count': dbg_try_count,
1311 'delta_try_count': dbg_try_count,
1310 'type': dbg_type,
1312 'type': dbg_type,
1311 'p1-chain-len': p1_chain_len,
1313 'p1-chain-len': p1_chain_len,
1312 'p2-chain-len': p2_chain_len,
1314 'p2-chain-len': p2_chain_len,
1313 }
1315 }
1314 if (
1316 if (
1315 deltainfo.snapshotdepth # pytype: disable=attribute-error
1317 deltainfo.snapshotdepth # pytype: disable=attribute-error
1316 is not None
1318 is not None
1317 ):
1319 ):
1318 dbg[
1320 dbg[
1319 'snapshot-depth'
1321 'snapshot-depth'
1320 ] = deltainfo.snapshotdepth # pytype: disable=attribute-error
1322 ] = deltainfo.snapshotdepth # pytype: disable=attribute-error
1321 else:
1323 else:
1322 dbg['snapshot-depth'] = 0
1324 dbg['snapshot-depth'] = 0
1323 target_revlog = b"UNKNOWN"
1325 target_revlog = b"UNKNOWN"
1324 target_type = self.revlog.target[0]
1326 target_type = self.revlog.target[0]
1325 target_key = self.revlog.target[1]
1327 target_key = self.revlog.target[1]
1326 if target_type == KIND_CHANGELOG:
1328 if target_type == KIND_CHANGELOG:
1327 target_revlog = b'CHANGELOG:'
1329 target_revlog = b'CHANGELOG:'
1328 elif target_type == KIND_MANIFESTLOG:
1330 elif target_type == KIND_MANIFESTLOG:
1329 target_revlog = b'MANIFESTLOG:'
1331 target_revlog = b'MANIFESTLOG:'
1330 if target_key:
1332 if target_key:
1331 target_revlog += b'%s:' % target_key
1333 target_revlog += b'%s:' % target_key
1332 elif target_type == KIND_FILELOG:
1334 elif target_type == KIND_FILELOG:
1333 target_revlog = b'FILELOG:'
1335 target_revlog = b'FILELOG:'
1334 if target_key:
1336 if target_key:
1335 target_revlog += b'%s:' % target_key
1337 target_revlog += b'%s:' % target_key
1336 dbg['target-revlog'] = target_revlog
1338 dbg['target-revlog'] = target_revlog
1337
1339
1338 if self._debug_info is not None:
1340 if self._debug_info is not None:
1339 self._debug_info.append(dbg)
1341 self._debug_info.append(dbg)
1340
1342
1341 if self._write_debug is not None:
1343 if self._write_debug is not None:
1342 msg = (
1344 msg = (
1343 b"DBG-DELTAS:"
1345 b"DBG-DELTAS:"
1344 b" %-12s"
1346 b" %-12s"
1345 b" rev=%d:"
1347 b" rev=%d:"
1346 b" delta-base=%d"
1348 b" delta-base=%d"
1347 b" is-cached=%d"
1349 b" is-cached=%d"
1348 b" - search-rounds=%d"
1350 b" - search-rounds=%d"
1349 b" try-count=%d"
1351 b" try-count=%d"
1350 b" - delta-type=%-6s"
1352 b" - delta-type=%-6s"
1351 b" snap-depth=%d"
1353 b" snap-depth=%d"
1352 b" - p1-chain-length=%d"
1354 b" - p1-chain-length=%d"
1353 b" p2-chain-length=%d"
1355 b" p2-chain-length=%d"
1354 b" - duration=%f"
1356 b" - duration=%f"
1355 b"\n"
1357 b"\n"
1356 )
1358 )
1357 msg %= (
1359 msg %= (
1358 dbg["target-revlog"],
1360 dbg["target-revlog"],
1359 dbg["revision"],
1361 dbg["revision"],
1360 dbg["delta-base"],
1362 dbg["delta-base"],
1361 dbg["using-cached-base"],
1363 dbg["using-cached-base"],
1362 dbg["search_round_count"],
1364 dbg["search_round_count"],
1363 dbg["delta_try_count"],
1365 dbg["delta_try_count"],
1364 dbg["type"],
1366 dbg["type"],
1365 dbg["snapshot-depth"],
1367 dbg["snapshot-depth"],
1366 dbg["p1-chain-len"],
1368 dbg["p1-chain-len"],
1367 dbg["p2-chain-len"],
1369 dbg["p2-chain-len"],
1368 dbg["duration"],
1370 dbg["duration"],
1369 )
1371 )
1370 self._write_debug(msg)
1372 self._write_debug(msg)
1371 return deltainfo
1373 return deltainfo
1372
1374
1373
1375
1374 def delta_compression(default_compression_header, deltainfo):
1376 def delta_compression(default_compression_header, deltainfo):
1375 """return (COMPRESSION_MODE, deltainfo)
1377 """return (COMPRESSION_MODE, deltainfo)
1376
1378
1377 used by revlog v2+ format to dispatch between PLAIN and DEFAULT
1379 used by revlog v2+ format to dispatch between PLAIN and DEFAULT
1378 compression.
1380 compression.
1379 """
1381 """
1380 h, d = deltainfo.data
1382 h, d = deltainfo.data
1381 compression_mode = COMP_MODE_INLINE
1383 compression_mode = COMP_MODE_INLINE
1382 if not h and not d:
1384 if not h and not d:
1383 # not data to store at all... declare them uncompressed
1385 # not data to store at all... declare them uncompressed
1384 compression_mode = COMP_MODE_PLAIN
1386 compression_mode = COMP_MODE_PLAIN
1385 elif not h:
1387 elif not h:
1386 t = d[0:1]
1388 t = d[0:1]
1387 if t == b'\0':
1389 if t == b'\0':
1388 compression_mode = COMP_MODE_PLAIN
1390 compression_mode = COMP_MODE_PLAIN
1389 elif t == default_compression_header:
1391 elif t == default_compression_header:
1390 compression_mode = COMP_MODE_DEFAULT
1392 compression_mode = COMP_MODE_DEFAULT
1391 elif h == b'u':
1393 elif h == b'u':
1392 # we have a more efficient way to declare uncompressed
1394 # we have a more efficient way to declare uncompressed
1393 h = b''
1395 h = b''
1394 compression_mode = COMP_MODE_PLAIN
1396 compression_mode = COMP_MODE_PLAIN
1395 deltainfo = drop_u_compression(deltainfo)
1397 deltainfo = drop_u_compression(deltainfo)
1396 return compression_mode, deltainfo
1398 return compression_mode, deltainfo
@@ -1,346 +1,341 b''
1 ====================================
1 ====================================
2 Test delta choice with sparse revlog
2 Test delta choice with sparse revlog
3 ====================================
3 ====================================
4
4
5 Sparse-revlog usually shows the most gain on Manifest. However, it is simpler
5 Sparse-revlog usually shows the most gain on Manifest. However, it is simpler
6 to general an appropriate file, so we test with a single file instead. The
6 to general an appropriate file, so we test with a single file instead. The
7 goal is to observe intermediate snapshot being created.
7 goal is to observe intermediate snapshot being created.
8
8
9 We need a large enough file. Part of the content needs to be replaced
9 We need a large enough file. Part of the content needs to be replaced
10 repeatedly while some of it changes rarely.
10 repeatedly while some of it changes rarely.
11
11
12 $ bundlepath="$TESTDIR/artifacts/cache/big-file-churn.hg"
12 $ bundlepath="$TESTDIR/artifacts/cache/big-file-churn.hg"
13
13
14 $ expectedhash=`cat "$bundlepath".md5`
14 $ expectedhash=`cat "$bundlepath".md5`
15
15
16 #if slow
16 #if slow
17
17
18 $ if [ ! -f "$bundlepath" ]; then
18 $ if [ ! -f "$bundlepath" ]; then
19 > "$TESTDIR"/artifacts/scripts/generate-churning-bundle.py > /dev/null
19 > "$TESTDIR"/artifacts/scripts/generate-churning-bundle.py > /dev/null
20 > fi
20 > fi
21
21
22 #else
22 #else
23
23
24 $ if [ ! -f "$bundlepath" ]; then
24 $ if [ ! -f "$bundlepath" ]; then
25 > echo 'skipped: missing artifact, run "'"$TESTDIR"'/artifacts/scripts/generate-churning-bundle.py"'
25 > echo 'skipped: missing artifact, run "'"$TESTDIR"'/artifacts/scripts/generate-churning-bundle.py"'
26 > exit 80
26 > exit 80
27 > fi
27 > fi
28
28
29 #endif
29 #endif
30
30
31 $ currenthash=`f -M "$bundlepath" | cut -d = -f 2`
31 $ currenthash=`f -M "$bundlepath" | cut -d = -f 2`
32 $ if [ "$currenthash" != "$expectedhash" ]; then
32 $ if [ "$currenthash" != "$expectedhash" ]; then
33 > echo 'skipped: outdated artifact, md5 "'"$currenthash"'" expected "'"$expectedhash"'" run "'"$TESTDIR"'/artifacts/scripts/generate-churning-bundle.py"'
33 > echo 'skipped: outdated artifact, md5 "'"$currenthash"'" expected "'"$expectedhash"'" run "'"$TESTDIR"'/artifacts/scripts/generate-churning-bundle.py"'
34 > exit 80
34 > exit 80
35 > fi
35 > fi
36
36
37 $ cat >> $HGRCPATH << EOF
37 $ cat >> $HGRCPATH << EOF
38 > [format]
38 > [format]
39 > sparse-revlog = yes
39 > sparse-revlog = yes
40 > maxchainlen = 15
40 > maxchainlen = 15
41 > [storage]
41 > [storage]
42 > revlog.optimize-delta-parent-choice = yes
42 > revlog.optimize-delta-parent-choice = yes
43 > revlog.reuse-external-delta = no
43 > revlog.reuse-external-delta = no
44 > EOF
44 > EOF
45 $ hg init sparse-repo
45 $ hg init sparse-repo
46 $ cd sparse-repo
46 $ cd sparse-repo
47 $ hg unbundle $bundlepath
47 $ hg unbundle $bundlepath
48 adding changesets
48 adding changesets
49 adding manifests
49 adding manifests
50 adding file changes
50 adding file changes
51 added 5001 changesets with 5001 changes to 1 files (+89 heads)
51 added 5001 changesets with 5001 changes to 1 files (+89 heads)
52 new changesets 9706f5af64f4:d9032adc8114 (5001 drafts)
52 new changesets 9706f5af64f4:d9032adc8114 (5001 drafts)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
54 $ hg up
54 $ hg up
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 updated to "d9032adc8114: commit #5000"
56 updated to "d9032adc8114: commit #5000"
57 89 other heads for branch "default"
57 89 other heads for branch "default"
58
58
59 $ hg log --stat -r 0:3
59 $ hg log --stat -r 0:3
60 changeset: 0:9706f5af64f4
60 changeset: 0:9706f5af64f4
61 user: test
61 user: test
62 date: Thu Jan 01 00:00:00 1970 +0000
62 date: Thu Jan 01 00:00:00 1970 +0000
63 summary: initial commit
63 summary: initial commit
64
64
65 SPARSE-REVLOG-TEST-FILE | 10500 ++++++++++++++++++++++++++++++++++++++++++++++
65 SPARSE-REVLOG-TEST-FILE | 10500 ++++++++++++++++++++++++++++++++++++++++++++++
66 1 files changed, 10500 insertions(+), 0 deletions(-)
66 1 files changed, 10500 insertions(+), 0 deletions(-)
67
67
68 changeset: 1:724907deaa5e
68 changeset: 1:724907deaa5e
69 user: test
69 user: test
70 date: Thu Jan 01 00:00:00 1970 +0000
70 date: Thu Jan 01 00:00:00 1970 +0000
71 summary: commit #1
71 summary: commit #1
72
72
73 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
73 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
74 1 files changed, 534 insertions(+), 534 deletions(-)
74 1 files changed, 534 insertions(+), 534 deletions(-)
75
75
76 changeset: 2:62c41bce3e5d
76 changeset: 2:62c41bce3e5d
77 user: test
77 user: test
78 date: Thu Jan 01 00:00:00 1970 +0000
78 date: Thu Jan 01 00:00:00 1970 +0000
79 summary: commit #2
79 summary: commit #2
80
80
81 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
81 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
82 1 files changed, 534 insertions(+), 534 deletions(-)
82 1 files changed, 534 insertions(+), 534 deletions(-)
83
83
84 changeset: 3:348a9cbd6959
84 changeset: 3:348a9cbd6959
85 user: test
85 user: test
86 date: Thu Jan 01 00:00:00 1970 +0000
86 date: Thu Jan 01 00:00:00 1970 +0000
87 summary: commit #3
87 summary: commit #3
88
88
89 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
89 SPARSE-REVLOG-TEST-FILE | 1068 +++++++++++++++++++++++-----------------------
90 1 files changed, 534 insertions(+), 534 deletions(-)
90 1 files changed, 534 insertions(+), 534 deletions(-)
91
91
92
92
93 $ f -s .hg/store/data/*.d
93 $ f -s .hg/store/data/*.d
94 .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=58616973
94 .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=58616973
95 $ hg debugrevlog *
95 $ hg debugrevlog *
96 format : 1
96 format : 1
97 flags : generaldelta
97 flags : generaldelta
98
98
99 revisions : 5001
99 revisions : 5001
100 merges : 625 (12.50%)
100 merges : 625 (12.50%)
101 normal : 4376 (87.50%)
101 normal : 4376 (87.50%)
102 revisions : 5001
102 revisions : 5001
103 empty : 0 ( 0.00%)
103 empty : 0 ( 0.00%)
104 text : 0 (100.00%)
104 text : 0 (100.00%)
105 delta : 0 (100.00%)
105 delta : 0 (100.00%)
106 snapshot : 383 ( 7.66%)
106 snapshot : 383 ( 7.66%)
107 lvl-0 : 3 ( 0.06%)
107 lvl-0 : 3 ( 0.06%)
108 lvl-1 : 18 ( 0.36%)
108 lvl-1 : 18 ( 0.36%)
109 lvl-2 : 62 ( 1.24%)
109 lvl-2 : 62 ( 1.24%)
110 lvl-3 : 108 ( 2.16%)
110 lvl-3 : 108 ( 2.16%)
111 lvl-4 : 191 ( 3.82%)
111 lvl-4 : 191 ( 3.82%)
112 lvl-5 : 1 ( 0.02%)
112 lvl-5 : 1 ( 0.02%)
113 deltas : 4618 (92.34%)
113 deltas : 4618 (92.34%)
114 revision size : 58616973
114 revision size : 58616973
115 snapshot : 9247844 (15.78%)
115 snapshot : 9247844 (15.78%)
116 lvl-0 : 539532 ( 0.92%)
116 lvl-0 : 539532 ( 0.92%)
117 lvl-1 : 1467743 ( 2.50%)
117 lvl-1 : 1467743 ( 2.50%)
118 lvl-2 : 1873820 ( 3.20%)
118 lvl-2 : 1873820 ( 3.20%)
119 lvl-3 : 2326874 ( 3.97%)
119 lvl-3 : 2326874 ( 3.97%)
120 lvl-4 : 3029118 ( 5.17%)
120 lvl-4 : 3029118 ( 5.17%)
121 lvl-5 : 10757 ( 0.02%)
121 lvl-5 : 10757 ( 0.02%)
122 deltas : 49369129 (84.22%)
122 deltas : 49369129 (84.22%)
123
123
124 chunks : 5001
124 chunks : 5001
125 0x28 : 5001 (100.00%)
125 0x28 : 5001 (100.00%)
126 chunks size : 58616973
126 chunks size : 58616973
127 0x28 : 58616973 (100.00%)
127 0x28 : 58616973 (100.00%)
128
128
129 avg chain length : 9
129 avg chain length : 9
130 max chain length : 15
130 max chain length : 15
131 max chain reach : 27366701
131 max chain reach : 27366701
132 compression ratio : 29
132 compression ratio : 29
133
133
134 uncompressed data size (min/max/avg) : 346468 / 346472 / 346471
134 uncompressed data size (min/max/avg) : 346468 / 346472 / 346471
135 full revision size (min/max/avg) : 179288 / 180786 / 179844
135 full revision size (min/max/avg) : 179288 / 180786 / 179844
136 inter-snapshot size (min/max/avg) : 10757 / 169507 / 22916
136 inter-snapshot size (min/max/avg) : 10757 / 169507 / 22916
137 level-1 (min/max/avg) : 13905 / 169507 / 81541
137 level-1 (min/max/avg) : 13905 / 169507 / 81541
138 level-2 (min/max/avg) : 10887 / 83873 / 30222
138 level-2 (min/max/avg) : 10887 / 83873 / 30222
139 level-3 (min/max/avg) : 10911 / 43047 / 21545
139 level-3 (min/max/avg) : 10911 / 43047 / 21545
140 level-4 (min/max/avg) : 10838 / 21390 / 15859
140 level-4 (min/max/avg) : 10838 / 21390 / 15859
141 level-5 (min/max/avg) : 10757 / 10757 / 10757
141 level-5 (min/max/avg) : 10757 / 10757 / 10757
142 delta size (min/max/avg) : 9672 / 108072 / 10690
142 delta size (min/max/avg) : 9672 / 108072 / 10690
143
143
144 deltas against prev : 3906 (84.58%)
144 deltas against prev : 3906 (84.58%)
145 where prev = p1 : 3906 (100.00%)
145 where prev = p1 : 3906 (100.00%)
146 where prev = p2 : 0 ( 0.00%)
146 where prev = p2 : 0 ( 0.00%)
147 other : 0 ( 0.00%)
147 other : 0 ( 0.00%)
148 deltas against p1 : 649 (14.05%)
148 deltas against p1 : 649 (14.05%)
149 deltas against p2 : 63 ( 1.36%)
149 deltas against p2 : 63 ( 1.36%)
150 deltas against other : 0 ( 0.00%)
150 deltas against other : 0 ( 0.00%)
151
151
152
152
153 Test `debug-delta-find`
153 Test `debug-delta-find`
154 -----------------------
154 -----------------------
155
155
156 $ ls -1
156 $ ls -1
157 SPARSE-REVLOG-TEST-FILE
157 SPARSE-REVLOG-TEST-FILE
158 $ hg debugdeltachain SPARSE-REVLOG-TEST-FILE | grep snap | tail -1
158 $ hg debugdeltachain SPARSE-REVLOG-TEST-FILE | grep snap | tail -1
159 4971 4970 -1 3 5 4930 snap 19179 346472 427596 1.23414 15994877 15567281 36.40652 427596 179288 1.00000 5
159 4971 4970 -1 3 5 4930 snap 19179 346472 427596 1.23414 15994877 15567281 36.40652 427596 179288 1.00000 5
160 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971
160 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971
161 DBG-DELTAS-SEARCH: SEARCH rev=4971
161 DBG-DELTAS-SEARCH: SEARCH rev=4971
162 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
162 DBG-DELTAS-SEARCH: ROUND #1 - 1 candidates - search-down
163 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
163 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
164 DBG-DELTAS-SEARCH: type=snapshot-4
164 DBG-DELTAS-SEARCH: type=snapshot-4
165 DBG-DELTAS-SEARCH: size=18296
165 DBG-DELTAS-SEARCH: size=18296
166 DBG-DELTAS-SEARCH: base=4930
166 DBG-DELTAS-SEARCH: base=4930
167 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
167 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
168 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
168 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
169 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
169 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
170 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
171 DBG-DELTAS-SEARCH: type=snapshot-4
172 DBG-DELTAS-SEARCH: size=19179
173 DBG-DELTAS-SEARCH: base=4930
174 DBG-DELTAS-SEARCH: TOO-HIGH
175 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
170 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
176 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
171 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
177 DBG-DELTAS-SEARCH: type=snapshot-3
172 DBG-DELTAS-SEARCH: type=snapshot-3
178 DBG-DELTAS-SEARCH: size=39228
173 DBG-DELTAS-SEARCH: size=39228
179 DBG-DELTAS-SEARCH: base=4799
174 DBG-DELTAS-SEARCH: base=4799
180 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
175 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
181 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
176 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
182 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
177 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
183 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
178 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
184 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
179 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
185 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
180 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
186 DBG-DELTAS-SEARCH: type=snapshot-2
181 DBG-DELTAS-SEARCH: type=snapshot-2
187 DBG-DELTAS-SEARCH: size=50213
182 DBG-DELTAS-SEARCH: size=50213
188 DBG-DELTAS-SEARCH: base=4623
183 DBG-DELTAS-SEARCH: base=4623
189 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
184 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
190 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
185 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
191 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
186 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
192 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
187 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
193
188
194 $ cat << EOF >>.hg/hgrc
189 $ cat << EOF >>.hg/hgrc
195 > [storage]
190 > [storage]
196 > revlog.optimize-delta-parent-choice = no
191 > revlog.optimize-delta-parent-choice = no
197 > revlog.reuse-external-delta = yes
192 > revlog.reuse-external-delta = yes
198 > EOF
193 > EOF
199
194
200 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --quiet
195 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --quiet
201 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
196 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
202 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source full
197 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source full
203 DBG-DELTAS-SEARCH: SEARCH rev=4971
198 DBG-DELTAS-SEARCH: SEARCH rev=4971
204 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
199 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
205 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
200 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
206 DBG-DELTAS-SEARCH: type=snapshot-4
201 DBG-DELTAS-SEARCH: type=snapshot-4
207 DBG-DELTAS-SEARCH: size=18296
202 DBG-DELTAS-SEARCH: size=18296
208 DBG-DELTAS-SEARCH: base=4930
203 DBG-DELTAS-SEARCH: base=4930
209 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
204 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
210 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
205 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
211 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
206 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
212 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
207 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
213 DBG-DELTAS-SEARCH: type=snapshot-4
208 DBG-DELTAS-SEARCH: type=snapshot-4
214 DBG-DELTAS-SEARCH: size=19179
209 DBG-DELTAS-SEARCH: size=19179
215 DBG-DELTAS-SEARCH: base=4930
210 DBG-DELTAS-SEARCH: base=4930
216 DBG-DELTAS-SEARCH: TOO-HIGH
211 DBG-DELTAS-SEARCH: TOO-HIGH
217 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
212 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
218 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
213 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
219 DBG-DELTAS-SEARCH: type=snapshot-3
214 DBG-DELTAS-SEARCH: type=snapshot-3
220 DBG-DELTAS-SEARCH: size=39228
215 DBG-DELTAS-SEARCH: size=39228
221 DBG-DELTAS-SEARCH: base=4799
216 DBG-DELTAS-SEARCH: base=4799
222 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
217 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
223 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
218 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
224 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
219 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
225 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
220 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
226 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
221 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
227 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
222 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
228 DBG-DELTAS-SEARCH: type=snapshot-2
223 DBG-DELTAS-SEARCH: type=snapshot-2
229 DBG-DELTAS-SEARCH: size=50213
224 DBG-DELTAS-SEARCH: size=50213
230 DBG-DELTAS-SEARCH: base=4623
225 DBG-DELTAS-SEARCH: base=4623
231 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
226 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
232 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
227 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
233 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
228 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
234 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
229 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
235 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source storage
230 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source storage
236 DBG-DELTAS-SEARCH: SEARCH rev=4971
231 DBG-DELTAS-SEARCH: SEARCH rev=4971
237 DBG-DELTAS-SEARCH: ROUND #1 - 1 candidates - cached-delta
232 DBG-DELTAS-SEARCH: ROUND #1 - 1 candidates - cached-delta
238 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
233 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
239 DBG-DELTAS-SEARCH: type=snapshot-3
234 DBG-DELTAS-SEARCH: type=snapshot-3
240 DBG-DELTAS-SEARCH: size=39228
235 DBG-DELTAS-SEARCH: size=39228
241 DBG-DELTAS-SEARCH: base=4799
236 DBG-DELTAS-SEARCH: base=4799
242 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
237 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
243 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
238 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
244 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
239 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
245 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=1 - search-rounds=1 try-count=1 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
240 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=1 - search-rounds=1 try-count=1 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
246 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source p1
241 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source p1
247 DBG-DELTAS-SEARCH: SEARCH rev=4971
242 DBG-DELTAS-SEARCH: SEARCH rev=4971
248 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
243 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
249 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
244 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
250 DBG-DELTAS-SEARCH: type=snapshot-4
245 DBG-DELTAS-SEARCH: type=snapshot-4
251 DBG-DELTAS-SEARCH: size=18296
246 DBG-DELTAS-SEARCH: size=18296
252 DBG-DELTAS-SEARCH: base=4930
247 DBG-DELTAS-SEARCH: base=4930
253 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
248 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
254 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
249 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
255 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
250 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
256 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
251 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
257 DBG-DELTAS-SEARCH: type=snapshot-4
252 DBG-DELTAS-SEARCH: type=snapshot-4
258 DBG-DELTAS-SEARCH: size=19179
253 DBG-DELTAS-SEARCH: size=19179
259 DBG-DELTAS-SEARCH: base=4930
254 DBG-DELTAS-SEARCH: base=4930
260 DBG-DELTAS-SEARCH: TOO-HIGH
255 DBG-DELTAS-SEARCH: TOO-HIGH
261 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
256 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
262 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
257 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
263 DBG-DELTAS-SEARCH: type=snapshot-3
258 DBG-DELTAS-SEARCH: type=snapshot-3
264 DBG-DELTAS-SEARCH: size=39228
259 DBG-DELTAS-SEARCH: size=39228
265 DBG-DELTAS-SEARCH: base=4799
260 DBG-DELTAS-SEARCH: base=4799
266 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
261 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
267 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
262 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
268 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
263 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
269 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
264 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
270 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
265 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
271 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
266 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
272 DBG-DELTAS-SEARCH: type=snapshot-2
267 DBG-DELTAS-SEARCH: type=snapshot-2
273 DBG-DELTAS-SEARCH: size=50213
268 DBG-DELTAS-SEARCH: size=50213
274 DBG-DELTAS-SEARCH: base=4623
269 DBG-DELTAS-SEARCH: base=4623
275 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
270 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
276 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
271 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
277 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
272 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
278 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
273 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
279 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source p2
274 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source p2
280 DBG-DELTAS-SEARCH: SEARCH rev=4971
275 DBG-DELTAS-SEARCH: SEARCH rev=4971
281 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
276 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
282 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
277 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
283 DBG-DELTAS-SEARCH: type=snapshot-4
278 DBG-DELTAS-SEARCH: type=snapshot-4
284 DBG-DELTAS-SEARCH: size=18296
279 DBG-DELTAS-SEARCH: size=18296
285 DBG-DELTAS-SEARCH: base=4930
280 DBG-DELTAS-SEARCH: base=4930
286 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
281 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
287 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
282 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
288 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
283 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
289 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
284 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
290 DBG-DELTAS-SEARCH: type=snapshot-4
285 DBG-DELTAS-SEARCH: type=snapshot-4
291 DBG-DELTAS-SEARCH: size=19179
286 DBG-DELTAS-SEARCH: size=19179
292 DBG-DELTAS-SEARCH: base=4930
287 DBG-DELTAS-SEARCH: base=4930
293 DBG-DELTAS-SEARCH: TOO-HIGH
288 DBG-DELTAS-SEARCH: TOO-HIGH
294 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
289 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
295 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
290 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
296 DBG-DELTAS-SEARCH: type=snapshot-3
291 DBG-DELTAS-SEARCH: type=snapshot-3
297 DBG-DELTAS-SEARCH: size=39228
292 DBG-DELTAS-SEARCH: size=39228
298 DBG-DELTAS-SEARCH: base=4799
293 DBG-DELTAS-SEARCH: base=4799
299 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
294 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
300 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
295 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
301 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
296 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
302 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
297 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
303 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
298 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
304 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
299 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
305 DBG-DELTAS-SEARCH: type=snapshot-2
300 DBG-DELTAS-SEARCH: type=snapshot-2
306 DBG-DELTAS-SEARCH: size=50213
301 DBG-DELTAS-SEARCH: size=50213
307 DBG-DELTAS-SEARCH: base=4623
302 DBG-DELTAS-SEARCH: base=4623
308 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
303 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
309 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
304 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
310 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
305 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
311 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
306 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
312 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source prev
307 $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 --source prev
313 DBG-DELTAS-SEARCH: SEARCH rev=4971
308 DBG-DELTAS-SEARCH: SEARCH rev=4971
314 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
309 DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down
315 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
310 DBG-DELTAS-SEARCH: CANDIDATE: rev=4962
316 DBG-DELTAS-SEARCH: type=snapshot-4
311 DBG-DELTAS-SEARCH: type=snapshot-4
317 DBG-DELTAS-SEARCH: size=18296
312 DBG-DELTAS-SEARCH: size=18296
318 DBG-DELTAS-SEARCH: base=4930
313 DBG-DELTAS-SEARCH: base=4930
319 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
314 DBG-DELTAS-SEARCH: uncompressed-delta-size=30377
320 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
315 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
321 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
316 DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD)
322 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
317 DBG-DELTAS-SEARCH: CANDIDATE: rev=4971
323 DBG-DELTAS-SEARCH: type=snapshot-4
318 DBG-DELTAS-SEARCH: type=snapshot-4
324 DBG-DELTAS-SEARCH: size=19179
319 DBG-DELTAS-SEARCH: size=19179
325 DBG-DELTAS-SEARCH: base=4930
320 DBG-DELTAS-SEARCH: base=4930
326 DBG-DELTAS-SEARCH: TOO-HIGH
321 DBG-DELTAS-SEARCH: TOO-HIGH
327 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
322 DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down
328 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
323 DBG-DELTAS-SEARCH: CANDIDATE: rev=4930
329 DBG-DELTAS-SEARCH: type=snapshot-3
324 DBG-DELTAS-SEARCH: type=snapshot-3
330 DBG-DELTAS-SEARCH: size=39228
325 DBG-DELTAS-SEARCH: size=39228
331 DBG-DELTAS-SEARCH: base=4799
326 DBG-DELTAS-SEARCH: base=4799
332 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
327 DBG-DELTAS-SEARCH: uncompressed-delta-size=33050
333 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
328 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
334 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
329 DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD)
335 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
330 DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down
336 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
331 DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179
337 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
332 DBG-DELTAS-SEARCH: CANDIDATE: rev=4799
338 DBG-DELTAS-SEARCH: type=snapshot-2
333 DBG-DELTAS-SEARCH: type=snapshot-2
339 DBG-DELTAS-SEARCH: size=50213
334 DBG-DELTAS-SEARCH: size=50213
340 DBG-DELTAS-SEARCH: base=4623
335 DBG-DELTAS-SEARCH: base=4623
341 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
336 DBG-DELTAS-SEARCH: uncompressed-delta-size=82661
342 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
337 DBG-DELTAS-SEARCH: delta-search-time=* (glob)
343 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
338 DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD)
344 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
339 DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: delta-base=4930 is-cached=0 - search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob)
345
340
346 $ cd ..
341 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now