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