##// END OF EJS Templates
Add lazy{parser,index,map} to speed up processing of index files
mpm@selenic.com -
r76:d993ebd6 default
parent child Browse files
Show More
@@ -1,421 +1,473 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # This provides efficient delta storage with O(1) retrieve and append
3 # This provides efficient delta storage with O(1) retrieve and append
4 # and O(changes) merge between branches
4 # and O(changes) merge between branches
5 #
5 #
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
7 #
7 #
8 # This software may be used and distributed according to the terms
8 # This software may be used and distributed according to the terms
9 # of the GNU General Public License, incorporated herein by reference.
9 # of the GNU General Public License, incorporated herein by reference.
10
10
11 import zlib, struct, sha, os, tempfile, binascii
11 import zlib, struct, sha, os, tempfile, binascii
12 from mercurial import mdiff
12 from mercurial import mdiff
13
13
14 def hex(node): return binascii.hexlify(node)
14 def hex(node): return binascii.hexlify(node)
15 def bin(node): return binascii.unhexlify(node)
15 def bin(node): return binascii.unhexlify(node)
16
16
17 def compress(text):
17 def compress(text):
18 return zlib.compress(text)
18 return zlib.compress(text)
19
19
20 def decompress(bin):
20 def decompress(bin):
21 return zlib.decompress(bin)
21 return zlib.decompress(bin)
22
22
23 def hash(text, p1, p2):
23 def hash(text, p1, p2):
24 l = [p1, p2]
24 l = [p1, p2]
25 l.sort()
25 l.sort()
26 return sha.sha(l[0] + l[1] + text).digest()
26 return sha.sha(l[0] + l[1] + text).digest()
27
27
28 nullid = "\0" * 20
28 nullid = "\0" * 20
29 indexformat = ">4l20s20s20s"
29 indexformat = ">4l20s20s20s"
30
30
31 class lazyparser:
32 def __init__(self, data):
33 self.data = data
34 self.s = struct.calcsize(indexformat)
35 self.l = len(data)/self.s
36 self.index = [None] * self.l
37 self.map = {nullid: -1}
38
39 if 0:
40 n = 0
41 i = self.data
42 s = struct.calcsize(indexformat)
43 for f in xrange(0, len(i), s):
44 # offset, size, base, linkrev, p1, p2, nodeid
45 e = struct.unpack(indexformat, i[f:f + s])
46 self.map[e[6]] = n
47 self.index.append(e)
48 n += 1
49
50 def load(self, pos):
51 block = pos / 1000
52 i = block * 1000
53 end = min(self.l, i + 1000)
54 while i < end:
55 d = self.data[i * self.s: (i + 1) * self.s]
56 e = struct.unpack(indexformat, d)
57 self.index[i] = e
58 self.map[e[6]] = i
59 i += 1
60
61 class lazyindex:
62 def __init__(self, parser):
63 self.p = parser
64 def __len__(self):
65 return len(self.p.index)
66 def __getitem__(self, pos):
67 i = self.p.index[pos]
68 if not i:
69 self.p.load(pos)
70 return self.p.index[pos]
71 return i
72 def append(self, e):
73 self.p.index.append(e)
74
75 class lazymap:
76 def __init__(self, parser):
77 self.p = parser
78 def load(self, key):
79 n = self.p.data.find(key)
80 if n < 0: raise KeyError(key)
81 pos = n / self.p.s
82 self.p.load(pos)
83 def __contains__(self, key):
84 try:
85 self.p.map[key]
86 return True
87 except KeyError:
88 return False
89 def __getitem__(self, key):
90 try:
91 return self.p.map[key]
92 except KeyError:
93 self.load(key)
94 return self.p.map[key]
95 def __setitem__(self, key, val):
96 self.p.map[key] = val
97
31 class revlog:
98 class revlog:
32 def __init__(self, opener, indexfile, datafile):
99 def __init__(self, opener, indexfile, datafile):
33 self.indexfile = indexfile
100 self.indexfile = indexfile
34 self.datafile = datafile
101 self.datafile = datafile
35 self.index = []
36 self.opener = opener
102 self.opener = opener
37 self.cache = None
103 self.cache = None
38 # read the whole index for now, handle on-demand later
104 # read the whole index for now, handle on-demand later
39 try:
105 try:
40 n = 0
41 i = self.opener(self.indexfile).read()
106 i = self.opener(self.indexfile).read()
42 s = struct.calcsize(indexformat)
43
44 # preallocate arrays
45 l = len(i)/s
46 self.index = [None] * l
47 m = [None] * l
48
49 for f in xrange(0, len(i), s):
50 # offset, size, base, linkrev, p1, p2, nodeid
51 e = struct.unpack(indexformat, i[f:f + s])
52 self.index[n] = e
53 m[n] = (e[6], n)
54 n += 1
55
56 self.nodemap = dict(m)
57 except IOError:
107 except IOError:
58 self.nodemap = {}
108 i = ""
59 self.nodemap[nullid] = -1
109 parser = lazyparser(i)
110 self.index = lazyindex(parser)
111 self.nodemap = lazymap(parser)
60
112
61 def tip(self): return self.node(len(self.index) - 1)
113 def tip(self): return self.node(len(self.index) - 1)
62 def count(self): return len(self.index)
114 def count(self): return len(self.index)
63 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
115 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
64 def rev(self, node): return self.nodemap[node]
116 def rev(self, node): return self.nodemap[node]
65 def linkrev(self, node): return self.index[self.nodemap[node]][3]
117 def linkrev(self, node): return self.index[self.nodemap[node]][3]
66 def parents(self, node):
118 def parents(self, node):
67 if node == nullid: return (nullid, nullid)
119 if node == nullid: return (nullid, nullid)
68 return self.index[self.nodemap[node]][4:6]
120 return self.index[self.nodemap[node]][4:6]
69
121
70 def start(self, rev): return self.index[rev][0]
122 def start(self, rev): return self.index[rev][0]
71 def length(self, rev): return self.index[rev][1]
123 def length(self, rev): return self.index[rev][1]
72 def end(self, rev): return self.start(rev) + self.length(rev)
124 def end(self, rev): return self.start(rev) + self.length(rev)
73 def base(self, rev): return self.index[rev][2]
125 def base(self, rev): return self.index[rev][2]
74
126
75 def lookup(self, id):
127 def lookup(self, id):
76 try:
128 try:
77 rev = int(id)
129 rev = int(id)
78 return self.node(rev)
130 return self.node(rev)
79 except ValueError:
131 except ValueError:
80 c = []
132 c = []
81 for n in self.nodemap:
133 for n in self.nodemap:
82 if id in hex(n):
134 if id in hex(n):
83 c.append(n)
135 c.append(n)
84 if len(c) > 1: raise KeyError("Ambiguous identifier")
136 if len(c) > 1: raise KeyError("Ambiguous identifier")
85 if len(c) < 1: raise KeyError("No match found")
137 if len(c) < 1: raise KeyError("No match found")
86 return c[0]
138 return c[0]
87
139
88 return None
140 return None
89
141
90 def revisions(self, list):
142 def revisions(self, list):
91 # this can be optimized to do spans, etc
143 # this can be optimized to do spans, etc
92 # be stupid for now
144 # be stupid for now
93 for node in list:
145 for node in list:
94 yield self.revision(node)
146 yield self.revision(node)
95
147
96 def diff(self, a, b):
148 def diff(self, a, b):
97 return mdiff.textdiff(a, b)
149 return mdiff.textdiff(a, b)
98
150
99 def patches(self, t, pl):
151 def patches(self, t, pl):
100 return mdiff.patches(t, pl)
152 return mdiff.patches(t, pl)
101
153
102 def revision(self, node):
154 def revision(self, node):
103 if node == nullid: return ""
155 if node == nullid: return ""
104 if self.cache and self.cache[0] == node: return self.cache[2]
156 if self.cache and self.cache[0] == node: return self.cache[2]
105
157
106 text = None
158 text = None
107 rev = self.rev(node)
159 rev = self.rev(node)
108 base = self.base(rev)
160 base = self.base(rev)
109 start = self.start(base)
161 start = self.start(base)
110 end = self.end(rev)
162 end = self.end(rev)
111
163
112 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
164 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
113 base = self.cache[1]
165 base = self.cache[1]
114 start = self.start(base + 1)
166 start = self.start(base + 1)
115 text = self.cache[2]
167 text = self.cache[2]
116 last = 0
168 last = 0
117
169
118 f = self.opener(self.datafile)
170 f = self.opener(self.datafile)
119 f.seek(start)
171 f.seek(start)
120 data = f.read(end - start)
172 data = f.read(end - start)
121
173
122 if not text:
174 if not text:
123 last = self.length(base)
175 last = self.length(base)
124 text = decompress(data[:last])
176 text = decompress(data[:last])
125
177
126 bins = []
178 bins = []
127 for r in xrange(base + 1, rev + 1):
179 for r in xrange(base + 1, rev + 1):
128 s = self.length(r)
180 s = self.length(r)
129 bins.append(decompress(data[last:last + s]))
181 bins.append(decompress(data[last:last + s]))
130 last = last + s
182 last = last + s
131
183
132 text = mdiff.patches(text, bins)
184 text = mdiff.patches(text, bins)
133
185
134 (p1, p2) = self.parents(node)
186 (p1, p2) = self.parents(node)
135 if node != hash(text, p1, p2):
187 if node != hash(text, p1, p2):
136 raise "integrity check failed on %s:%d" % (self.datafile, rev)
188 raise "integrity check failed on %s:%d" % (self.datafile, rev)
137
189
138 self.cache = (node, rev, text)
190 self.cache = (node, rev, text)
139 return text
191 return text
140
192
141 def addrevision(self, text, transaction, link, p1=None, p2=None):
193 def addrevision(self, text, transaction, link, p1=None, p2=None):
142 if text is None: text = ""
194 if text is None: text = ""
143 if p1 is None: p1 = self.tip()
195 if p1 is None: p1 = self.tip()
144 if p2 is None: p2 = nullid
196 if p2 is None: p2 = nullid
145
197
146 node = hash(text, p1, p2)
198 node = hash(text, p1, p2)
147
199
148 n = self.count()
200 n = self.count()
149 t = n - 1
201 t = n - 1
150
202
151 if n:
203 if n:
152 base = self.base(t)
204 base = self.base(t)
153 start = self.start(base)
205 start = self.start(base)
154 end = self.end(t)
206 end = self.end(t)
155 prev = self.revision(self.tip())
207 prev = self.revision(self.tip())
156 data = compress(self.diff(prev, text))
208 data = compress(self.diff(prev, text))
157 dist = end - start + len(data)
209 dist = end - start + len(data)
158
210
159 # full versions are inserted when the needed deltas
211 # full versions are inserted when the needed deltas
160 # become comparable to the uncompressed text
212 # become comparable to the uncompressed text
161 if not n or dist > len(text) * 2:
213 if not n or dist > len(text) * 2:
162 data = compress(text)
214 data = compress(text)
163 base = n
215 base = n
164 else:
216 else:
165 base = self.base(t)
217 base = self.base(t)
166
218
167 offset = 0
219 offset = 0
168 if t >= 0:
220 if t >= 0:
169 offset = self.end(t)
221 offset = self.end(t)
170
222
171 e = (offset, len(data), base, link, p1, p2, node)
223 e = (offset, len(data), base, link, p1, p2, node)
172
224
173 self.index.append(e)
225 self.index.append(e)
174 self.nodemap[node] = n
226 self.nodemap[node] = n
175 entry = struct.pack(indexformat, *e)
227 entry = struct.pack(indexformat, *e)
176
228
177 transaction.add(self.datafile, e[0])
229 transaction.add(self.datafile, e[0])
178 self.opener(self.datafile, "a").write(data)
230 self.opener(self.datafile, "a").write(data)
179 transaction.add(self.indexfile, n * len(entry))
231 transaction.add(self.indexfile, n * len(entry))
180 self.opener(self.indexfile, "a").write(entry)
232 self.opener(self.indexfile, "a").write(entry)
181
233
182 self.cache = (node, n, text)
234 self.cache = (node, n, text)
183 return node
235 return node
184
236
185 def ancestor(self, a, b):
237 def ancestor(self, a, b):
186 def expand(list, map):
238 def expand(list, map):
187 a = []
239 a = []
188 while list:
240 while list:
189 n = list.pop(0)
241 n = list.pop(0)
190 map[n] = 1
242 map[n] = 1
191 yield n
243 yield n
192 for p in self.parents(n):
244 for p in self.parents(n):
193 if p != nullid and p not in map:
245 if p != nullid and p not in map:
194 list.append(p)
246 list.append(p)
195 yield nullid
247 yield nullid
196
248
197 amap = {}
249 amap = {}
198 bmap = {}
250 bmap = {}
199 ag = expand([a], amap)
251 ag = expand([a], amap)
200 bg = expand([b], bmap)
252 bg = expand([b], bmap)
201 adone = bdone = 0
253 adone = bdone = 0
202
254
203 while not adone or not bdone:
255 while not adone or not bdone:
204 if not adone:
256 if not adone:
205 an = ag.next()
257 an = ag.next()
206 if an == nullid:
258 if an == nullid:
207 adone = 1
259 adone = 1
208 elif an in bmap:
260 elif an in bmap:
209 return an
261 return an
210 if not bdone:
262 if not bdone:
211 bn = bg.next()
263 bn = bg.next()
212 if bn == nullid:
264 if bn == nullid:
213 bdone = 1
265 bdone = 1
214 elif bn in amap:
266 elif bn in amap:
215 return bn
267 return bn
216
268
217 return nullid
269 return nullid
218
270
219 def mergedag(self, other, transaction, linkseq, accumulate = None):
271 def mergedag(self, other, transaction, linkseq, accumulate = None):
220 """combine the nodes from other's DAG into ours"""
272 """combine the nodes from other's DAG into ours"""
221 old = self.tip()
273 old = self.tip()
222 i = self.count()
274 i = self.count()
223 l = []
275 l = []
224
276
225 # merge the other revision log into our DAG
277 # merge the other revision log into our DAG
226 for r in range(other.count()):
278 for r in range(other.count()):
227 id = other.node(r)
279 id = other.node(r)
228 if id not in self.nodemap:
280 if id not in self.nodemap:
229 (xn, yn) = other.parents(id)
281 (xn, yn) = other.parents(id)
230 l.append((id, xn, yn))
282 l.append((id, xn, yn))
231 self.nodemap[id] = i
283 self.nodemap[id] = i
232 i += 1
284 i += 1
233
285
234 # merge node date for new nodes
286 # merge node date for new nodes
235 r = other.revisions([e[0] for e in l])
287 r = other.revisions([e[0] for e in l])
236 for e in l:
288 for e in l:
237 t = r.next()
289 t = r.next()
238 if accumulate: accumulate(t)
290 if accumulate: accumulate(t)
239 self.addrevision(t, transaction, linkseq.next(), e[1], e[2])
291 self.addrevision(t, transaction, linkseq.next(), e[1], e[2])
240
292
241 # return the unmerged heads for later resolving
293 # return the unmerged heads for later resolving
242 return (old, self.tip())
294 return (old, self.tip())
243
295
244 def group(self, linkmap):
296 def group(self, linkmap):
245 # given a list of changeset revs, return a set of deltas and
297 # given a list of changeset revs, return a set of deltas and
246 # metadata corresponding to nodes the first delta is
298 # metadata corresponding to nodes the first delta is
247 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
299 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
248 # have this parent as it has all history before these
300 # have this parent as it has all history before these
249 # changesets. parent is parent[0]
301 # changesets. parent is parent[0]
250
302
251 revs = []
303 revs = []
252 needed = {}
304 needed = {}
253
305
254 # find file nodes/revs that match changeset revs
306 # find file nodes/revs that match changeset revs
255 for i in xrange(0, self.count()):
307 for i in xrange(0, self.count()):
256 if self.index[i][3] in linkmap:
308 if self.index[i][3] in linkmap:
257 revs.append(i)
309 revs.append(i)
258 needed[i] = 1
310 needed[i] = 1
259
311
260 # if we don't have any revisions touched by these changesets, bail
312 # if we don't have any revisions touched by these changesets, bail
261 if not revs: return struct.pack(">l", 0)
313 if not revs: return struct.pack(">l", 0)
262
314
263 # add the parent of the first rev
315 # add the parent of the first rev
264 p = self.parents(self.node(revs[0]))[0]
316 p = self.parents(self.node(revs[0]))[0]
265 revs.insert(0, self.rev(p))
317 revs.insert(0, self.rev(p))
266
318
267 # for each delta that isn't contiguous in the log, we need to
319 # for each delta that isn't contiguous in the log, we need to
268 # reconstruct the base, reconstruct the result, and then
320 # reconstruct the base, reconstruct the result, and then
269 # calculate the delta. We also need to do this where we've
321 # calculate the delta. We also need to do this where we've
270 # stored a full version and not a delta
322 # stored a full version and not a delta
271 for i in xrange(0, len(revs) - 1):
323 for i in xrange(0, len(revs) - 1):
272 a, b = revs[i], revs[i + 1]
324 a, b = revs[i], revs[i + 1]
273 if a + 1 != b or self.base(b) == b:
325 if a + 1 != b or self.base(b) == b:
274 for j in xrange(self.base(a), a + 1):
326 for j in xrange(self.base(a), a + 1):
275 needed[j] = 1
327 needed[j] = 1
276 for j in xrange(self.base(b), b + 1):
328 for j in xrange(self.base(b), b + 1):
277 needed[j] = 1
329 needed[j] = 1
278
330
279 # calculate spans to retrieve from datafile
331 # calculate spans to retrieve from datafile
280 needed = needed.keys()
332 needed = needed.keys()
281 needed.sort()
333 needed.sort()
282 spans = []
334 spans = []
283 for n in needed:
335 for n in needed:
284 if n < 0: continue
336 if n < 0: continue
285 o = self.start(n)
337 o = self.start(n)
286 l = self.length(n)
338 l = self.length(n)
287 spans.append((o, l, [(n, l)]))
339 spans.append((o, l, [(n, l)]))
288
340
289 # merge spans
341 # merge spans
290 merge = [spans.pop(0)]
342 merge = [spans.pop(0)]
291 while spans:
343 while spans:
292 e = spans.pop(0)
344 e = spans.pop(0)
293 f = merge[-1]
345 f = merge[-1]
294 if e[0] == f[0] + f[1]:
346 if e[0] == f[0] + f[1]:
295 merge[-1] = (f[0], f[1] + e[1], f[2] + e[2])
347 merge[-1] = (f[0], f[1] + e[1], f[2] + e[2])
296 else:
348 else:
297 merge.append(e)
349 merge.append(e)
298
350
299 # read spans in, divide up chunks
351 # read spans in, divide up chunks
300 chunks = {}
352 chunks = {}
301 for span in merge:
353 for span in merge:
302 # we reopen the file for each span to make http happy for now
354 # we reopen the file for each span to make http happy for now
303 f = self.opener(self.datafile)
355 f = self.opener(self.datafile)
304 f.seek(span[0])
356 f.seek(span[0])
305 data = f.read(span[1])
357 data = f.read(span[1])
306
358
307 # divide up the span
359 # divide up the span
308 pos = 0
360 pos = 0
309 for r, l in span[2]:
361 for r, l in span[2]:
310 chunks[r] = data[pos: pos + l]
362 chunks[r] = data[pos: pos + l]
311 pos += l
363 pos += l
312
364
313 # helper to reconstruct intermediate versions
365 # helper to reconstruct intermediate versions
314 def construct(text, base, rev):
366 def construct(text, base, rev):
315 bins = [decompress(chunks[r]) for r in xrange(base + 1, rev + 1)]
367 bins = [decompress(chunks[r]) for r in xrange(base + 1, rev + 1)]
316 return mdiff.patches(text, bins)
368 return mdiff.patches(text, bins)
317
369
318 # build deltas
370 # build deltas
319 deltas = []
371 deltas = []
320 for d in xrange(0, len(revs) - 1):
372 for d in xrange(0, len(revs) - 1):
321 a, b = revs[d], revs[d + 1]
373 a, b = revs[d], revs[d + 1]
322 n = self.node(b)
374 n = self.node(b)
323
375
324 if a + 1 != b or self.base(b) == b:
376 if a + 1 != b or self.base(b) == b:
325 if a >= 0:
377 if a >= 0:
326 base = self.base(a)
378 base = self.base(a)
327 ta = decompress(chunks[self.base(a)])
379 ta = decompress(chunks[self.base(a)])
328 ta = construct(ta, base, a)
380 ta = construct(ta, base, a)
329 else:
381 else:
330 ta = ""
382 ta = ""
331
383
332 base = self.base(b)
384 base = self.base(b)
333 if a > base:
385 if a > base:
334 base = a
386 base = a
335 tb = ta
387 tb = ta
336 else:
388 else:
337 tb = decompress(chunks[self.base(b)])
389 tb = decompress(chunks[self.base(b)])
338 tb = construct(tb, base, b)
390 tb = construct(tb, base, b)
339 d = self.diff(ta, tb)
391 d = self.diff(ta, tb)
340 else:
392 else:
341 d = decompress(chunks[b])
393 d = decompress(chunks[b])
342
394
343 p = self.parents(n)
395 p = self.parents(n)
344 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
396 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
345 l = struct.pack(">l", len(meta) + len(d) + 4)
397 l = struct.pack(">l", len(meta) + len(d) + 4)
346 deltas.append(l + meta + d)
398 deltas.append(l + meta + d)
347
399
348 l = struct.pack(">l", sum(map(len, deltas)) + 4)
400 l = struct.pack(">l", sum(map(len, deltas)) + 4)
349 deltas.insert(0, l)
401 deltas.insert(0, l)
350 return "".join(deltas)
402 return "".join(deltas)
351
403
352 def addgroup(self, data, linkmapper, transaction):
404 def addgroup(self, data, linkmapper, transaction):
353 # given a set of deltas, add them to the revision log. the
405 # given a set of deltas, add them to the revision log. the
354 # first delta is against its parent, which should be in our
406 # first delta is against its parent, which should be in our
355 # log, the rest are against the previous delta.
407 # log, the rest are against the previous delta.
356
408
357 if not data: return self.tip()
409 if not data: return self.tip()
358
410
359 # retrieve the parent revision of the delta chain
411 # retrieve the parent revision of the delta chain
360 chain = data[24:44]
412 chain = data[24:44]
361
413
362 # track the base of the current delta log
414 # track the base of the current delta log
363 r = self.count()
415 r = self.count()
364 t = r - 1
416 t = r - 1
365
417
366 base = prev = -1
418 base = prev = -1
367 start = end = 0
419 start = end = 0
368 if r:
420 if r:
369 start = self.start(self.base(t))
421 start = self.start(self.base(t))
370 end = self.end(t)
422 end = self.end(t)
371 measure = self.length(self.base(t))
423 measure = self.length(self.base(t))
372 base = self.base(t)
424 base = self.base(t)
373 prev = self.tip()
425 prev = self.tip()
374
426
375 transaction.add(self.datafile, end)
427 transaction.add(self.datafile, end)
376 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
428 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
377 dfh = self.opener(self.datafile, "a")
429 dfh = self.opener(self.datafile, "a")
378 ifh = self.opener(self.indexfile, "a")
430 ifh = self.opener(self.indexfile, "a")
379
431
380 # loop through our set of deltas
432 # loop through our set of deltas
381 pos = 0
433 pos = 0
382 while pos < len(data):
434 while pos < len(data):
383 l, node, p1, p2, cs = struct.unpack(">l20s20s20s20s",
435 l, node, p1, p2, cs = struct.unpack(">l20s20s20s20s",
384 data[pos:pos+84])
436 data[pos:pos+84])
385 link = linkmapper(cs)
437 link = linkmapper(cs)
386 delta = data[pos + 84:pos + l]
438 delta = data[pos + 84:pos + l]
387 pos += l
439 pos += l
388
440
389 # full versions are inserted when the needed deltas become
441 # full versions are inserted when the needed deltas become
390 # comparable to the uncompressed text or when the previous
442 # comparable to the uncompressed text or when the previous
391 # version is not the one we have a delta against. We use
443 # version is not the one we have a delta against. We use
392 # the size of the previous full rev as a proxy for the
444 # the size of the previous full rev as a proxy for the
393 # current size.
445 # current size.
394
446
395 if chain == prev:
447 if chain == prev:
396 cdelta = compress(delta)
448 cdelta = compress(delta)
397
449
398 if chain != prev or (end - start + len(cdelta)) > measure * 2:
450 if chain != prev or (end - start + len(cdelta)) > measure * 2:
399 # flush our writes here so we can read it in revision
451 # flush our writes here so we can read it in revision
400 dfh.flush()
452 dfh.flush()
401 ifh.flush()
453 ifh.flush()
402 text = self.revision(chain)
454 text = self.revision(chain)
403 text = self.patches(text, [delta])
455 text = self.patches(text, [delta])
404 chk = self.addrevision(text, transaction, link, p1, p2)
456 chk = self.addrevision(text, transaction, link, p1, p2)
405 if chk != node:
457 if chk != node:
406 raise "consistency error adding group"
458 raise "consistency error adding group"
407 measure = len(text)
459 measure = len(text)
408 else:
460 else:
409 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
461 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
410 self.index.append(e)
462 self.index.append(e)
411 self.nodemap[node] = r
463 self.nodemap[node] = r
412 dfh.write(cdelta)
464 dfh.write(cdelta)
413 ifh.write(struct.pack(indexformat, *e))
465 ifh.write(struct.pack(indexformat, *e))
414
466
415 t, r, chain, prev = r, r + 1, node, node
467 t, r, chain, prev = r, r + 1, node, node
416 start = self.start(self.base(t))
468 start = self.start(self.base(t))
417 end = self.end(t)
469 end = self.end(t)
418
470
419 dfh.close()
471 dfh.close()
420 ifh.close()
472 ifh.close()
421 return node
473 return node
General Comments 0
You need to be logged in to leave comments. Login now