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