##// END OF EJS Templates
fix heads for rev 0...
mpm@selenic.com -
r243:9a9ea2d1 default
parent child Browse files
Show More
@@ -1,510 +1,510 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, 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 heads(self):
159 def heads(self):
160 p = {}
160 p = {}
161 h = []
161 h = []
162 for r in range(self.count() - 1, 0, -1):
162 for r in range(self.count() - 1, -1, -1):
163 n = self.node(r)
163 n = self.node(r)
164 if n not in p:
164 if n not in p:
165 h.append(n)
165 h.append(n)
166 for pn in self.parents(n):
166 for pn in self.parents(n):
167 p[pn] = 1
167 p[pn] = 1
168 return h
168 return h
169
169
170 def lookup(self, id):
170 def lookup(self, id):
171 try:
171 try:
172 rev = int(id)
172 rev = int(id)
173 return self.node(rev)
173 return self.node(rev)
174 except ValueError:
174 except ValueError:
175 c = []
175 c = []
176 for n in self.nodemap:
176 for n in self.nodemap:
177 if id in hex(n):
177 if id in hex(n):
178 c.append(n)
178 c.append(n)
179 if len(c) > 1: raise KeyError("Ambiguous identifier")
179 if len(c) > 1: raise KeyError("Ambiguous identifier")
180 if len(c) < 1: raise KeyError("No match found")
180 if len(c) < 1: raise KeyError("No match found")
181 return c[0]
181 return c[0]
182
182
183 return None
183 return None
184
184
185 def diff(self, a, b):
185 def diff(self, a, b):
186 return mdiff.textdiff(a, b)
186 return mdiff.textdiff(a, b)
187
187
188 def patches(self, t, pl):
188 def patches(self, t, pl):
189 return mdiff.patches(t, pl)
189 return mdiff.patches(t, pl)
190
190
191 def delta(self, node):
191 def delta(self, node):
192 r = self.rev(node)
192 r = self.rev(node)
193 b = self.base(r)
193 b = self.base(r)
194 if r == b:
194 if r == b:
195 return self.diff(self.revision(self.node(r - 1)),
195 return self.diff(self.revision(self.node(r - 1)),
196 self.revision(node))
196 self.revision(node))
197 else:
197 else:
198 f = self.opener(self.datafile)
198 f = self.opener(self.datafile)
199 f.seek(self.start(r))
199 f.seek(self.start(r))
200 data = f.read(self.length(r))
200 data = f.read(self.length(r))
201 return decompress(data)
201 return decompress(data)
202
202
203 def revision(self, node):
203 def revision(self, node):
204 if node == nullid: return ""
204 if node == nullid: return ""
205 if self.cache and self.cache[0] == node: return self.cache[2]
205 if self.cache and self.cache[0] == node: return self.cache[2]
206
206
207 text = None
207 text = None
208 rev = self.rev(node)
208 rev = self.rev(node)
209 start, length, base, link, p1, p2, node = self.index[rev]
209 start, length, base, link, p1, p2, node = self.index[rev]
210 end = start + length
210 end = start + length
211 if base != rev: start = self.start(base)
211 if base != rev: start = self.start(base)
212
212
213 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
213 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
214 base = self.cache[1]
214 base = self.cache[1]
215 start = self.start(base + 1)
215 start = self.start(base + 1)
216 text = self.cache[2]
216 text = self.cache[2]
217 last = 0
217 last = 0
218
218
219 f = self.opener(self.datafile)
219 f = self.opener(self.datafile)
220 f.seek(start)
220 f.seek(start)
221 data = f.read(end - start)
221 data = f.read(end - start)
222
222
223 if not text:
223 if not text:
224 last = self.length(base)
224 last = self.length(base)
225 text = decompress(data[:last])
225 text = decompress(data[:last])
226
226
227 bins = []
227 bins = []
228 for r in xrange(base + 1, rev + 1):
228 for r in xrange(base + 1, rev + 1):
229 s = self.length(r)
229 s = self.length(r)
230 bins.append(decompress(data[last:last + s]))
230 bins.append(decompress(data[last:last + s]))
231 last = last + s
231 last = last + s
232
232
233 text = mdiff.patches(text, bins)
233 text = mdiff.patches(text, bins)
234
234
235 if node != hash(text, p1, p2):
235 if node != hash(text, p1, p2):
236 raise IOError("integrity check failed on %s:%d"
236 raise IOError("integrity check failed on %s:%d"
237 % (self.datafile, rev))
237 % (self.datafile, rev))
238
238
239 self.cache = (node, rev, text)
239 self.cache = (node, rev, text)
240 return text
240 return text
241
241
242 def addrevision(self, text, transaction, link, p1=None, p2=None):
242 def addrevision(self, text, transaction, link, p1=None, p2=None):
243 if text is None: text = ""
243 if text is None: text = ""
244 if p1 is None: p1 = self.tip()
244 if p1 is None: p1 = self.tip()
245 if p2 is None: p2 = nullid
245 if p2 is None: p2 = nullid
246
246
247 node = hash(text, p1, p2)
247 node = hash(text, p1, p2)
248
248
249 n = self.count()
249 n = self.count()
250 t = n - 1
250 t = n - 1
251
251
252 if n:
252 if n:
253 base = self.base(t)
253 base = self.base(t)
254 start = self.start(base)
254 start = self.start(base)
255 end = self.end(t)
255 end = self.end(t)
256 prev = self.revision(self.tip())
256 prev = self.revision(self.tip())
257 d = self.diff(prev, text)
257 d = self.diff(prev, text)
258 data = compress(d)
258 data = compress(d)
259 dist = end - start + len(data)
259 dist = end - start + len(data)
260
260
261 # full versions are inserted when the needed deltas
261 # full versions are inserted when the needed deltas
262 # become comparable to the uncompressed text
262 # become comparable to the uncompressed text
263 if not n or dist > len(text) * 2:
263 if not n or dist > len(text) * 2:
264 data = compress(text)
264 data = compress(text)
265 base = n
265 base = n
266 else:
266 else:
267 base = self.base(t)
267 base = self.base(t)
268
268
269 offset = 0
269 offset = 0
270 if t >= 0:
270 if t >= 0:
271 offset = self.end(t)
271 offset = self.end(t)
272
272
273 e = (offset, len(data), base, link, p1, p2, node)
273 e = (offset, len(data), base, link, p1, p2, node)
274
274
275 self.index.append(e)
275 self.index.append(e)
276 self.nodemap[node] = n
276 self.nodemap[node] = n
277 entry = struct.pack(indexformat, *e)
277 entry = struct.pack(indexformat, *e)
278
278
279 transaction.add(self.datafile, e[0])
279 transaction.add(self.datafile, e[0])
280 self.opener(self.datafile, "a").write(data)
280 self.opener(self.datafile, "a").write(data)
281 transaction.add(self.indexfile, n * len(entry))
281 transaction.add(self.indexfile, n * len(entry))
282 self.opener(self.indexfile, "a").write(entry)
282 self.opener(self.indexfile, "a").write(entry)
283
283
284 self.cache = (node, n, text)
284 self.cache = (node, n, text)
285 return node
285 return node
286
286
287 def ancestor(self, a, b):
287 def ancestor(self, a, b):
288 # calculate the distance of every node from root
288 # calculate the distance of every node from root
289 dist = {nullid: 0}
289 dist = {nullid: 0}
290 for i in xrange(self.count()):
290 for i in xrange(self.count()):
291 n = self.node(i)
291 n = self.node(i)
292 p1, p2 = self.parents(n)
292 p1, p2 = self.parents(n)
293 dist[n] = max(dist[p1], dist[p2]) + 1
293 dist[n] = max(dist[p1], dist[p2]) + 1
294
294
295 # traverse ancestors in order of decreasing distance from root
295 # traverse ancestors in order of decreasing distance from root
296 def ancestors(node):
296 def ancestors(node):
297 # we store negative distances because heap returns smallest member
297 # we store negative distances because heap returns smallest member
298 h = [(-dist[node], node)]
298 h = [(-dist[node], node)]
299 seen = {}
299 seen = {}
300 earliest = self.count()
300 earliest = self.count()
301 while h:
301 while h:
302 d, n = heapq.heappop(h)
302 d, n = heapq.heappop(h)
303 r = self.rev(n)
303 r = self.rev(n)
304 if n not in seen:
304 if n not in seen:
305 seen[n] = 1
305 seen[n] = 1
306 yield (-d, n)
306 yield (-d, n)
307 for p in self.parents(n):
307 for p in self.parents(n):
308 heapq.heappush(h, (-dist[p], p))
308 heapq.heappush(h, (-dist[p], p))
309
309
310 x = ancestors(a)
310 x = ancestors(a)
311 y = ancestors(b)
311 y = ancestors(b)
312 lx = x.next()
312 lx = x.next()
313 ly = y.next()
313 ly = y.next()
314
314
315 # increment each ancestor list until it is closer to root than
315 # increment each ancestor list until it is closer to root than
316 # the other, or they match
316 # the other, or they match
317 while 1:
317 while 1:
318 if lx == ly:
318 if lx == ly:
319 return lx[1]
319 return lx[1]
320 elif lx < ly:
320 elif lx < ly:
321 ly = y.next()
321 ly = y.next()
322 elif lx > ly:
322 elif lx > ly:
323 lx = x.next()
323 lx = x.next()
324
324
325 def group(self, linkmap):
325 def group(self, linkmap):
326 # given a list of changeset revs, return a set of deltas and
326 # given a list of changeset revs, return a set of deltas and
327 # metadata corresponding to nodes. the first delta is
327 # metadata corresponding to nodes. the first delta is
328 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
328 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
329 # have this parent as it has all history before these
329 # have this parent as it has all history before these
330 # changesets. parent is parent[0]
330 # changesets. parent is parent[0]
331
331
332 revs = []
332 revs = []
333 needed = {}
333 needed = {}
334
334
335 # find file nodes/revs that match changeset revs
335 # find file nodes/revs that match changeset revs
336 for i in xrange(0, self.count()):
336 for i in xrange(0, self.count()):
337 if self.index[i][3] in linkmap:
337 if self.index[i][3] in linkmap:
338 revs.append(i)
338 revs.append(i)
339 needed[i] = 1
339 needed[i] = 1
340
340
341 # if we don't have any revisions touched by these changesets, bail
341 # if we don't have any revisions touched by these changesets, bail
342 if not revs:
342 if not revs:
343 yield struct.pack(">l", 0)
343 yield struct.pack(">l", 0)
344 return
344 return
345
345
346 # add the parent of the first rev
346 # add the parent of the first rev
347 p = self.parents(self.node(revs[0]))[0]
347 p = self.parents(self.node(revs[0]))[0]
348 revs.insert(0, self.rev(p))
348 revs.insert(0, self.rev(p))
349
349
350 # for each delta that isn't contiguous in the log, we need to
350 # for each delta that isn't contiguous in the log, we need to
351 # reconstruct the base, reconstruct the result, and then
351 # reconstruct the base, reconstruct the result, and then
352 # calculate the delta. We also need to do this where we've
352 # calculate the delta. We also need to do this where we've
353 # stored a full version and not a delta
353 # stored a full version and not a delta
354 for i in xrange(0, len(revs) - 1):
354 for i in xrange(0, len(revs) - 1):
355 a, b = revs[i], revs[i + 1]
355 a, b = revs[i], revs[i + 1]
356 if a + 1 != b or self.base(b) == b:
356 if a + 1 != b or self.base(b) == b:
357 for j in xrange(self.base(a), a + 1):
357 for j in xrange(self.base(a), a + 1):
358 needed[j] = 1
358 needed[j] = 1
359 for j in xrange(self.base(b), b + 1):
359 for j in xrange(self.base(b), b + 1):
360 needed[j] = 1
360 needed[j] = 1
361
361
362 # calculate spans to retrieve from datafile
362 # calculate spans to retrieve from datafile
363 needed = needed.keys()
363 needed = needed.keys()
364 needed.sort()
364 needed.sort()
365 spans = []
365 spans = []
366 oo = -1
366 oo = -1
367 ol = 0
367 ol = 0
368 for n in needed:
368 for n in needed:
369 if n < 0: continue
369 if n < 0: continue
370 o = self.start(n)
370 o = self.start(n)
371 l = self.length(n)
371 l = self.length(n)
372 if oo + ol == o: # can we merge with the previous?
372 if oo + ol == o: # can we merge with the previous?
373 nl = spans[-1][2]
373 nl = spans[-1][2]
374 nl.append((n, l))
374 nl.append((n, l))
375 ol += l
375 ol += l
376 spans[-1] = (oo, ol, nl)
376 spans[-1] = (oo, ol, nl)
377 else:
377 else:
378 oo = o
378 oo = o
379 ol = l
379 ol = l
380 spans.append((oo, ol, [(n, l)]))
380 spans.append((oo, ol, [(n, l)]))
381
381
382 # read spans in, divide up chunks
382 # read spans in, divide up chunks
383 chunks = {}
383 chunks = {}
384 for span in spans:
384 for span in spans:
385 # we reopen the file for each span to make http happy for now
385 # we reopen the file for each span to make http happy for now
386 f = self.opener(self.datafile)
386 f = self.opener(self.datafile)
387 f.seek(span[0])
387 f.seek(span[0])
388 data = f.read(span[1])
388 data = f.read(span[1])
389
389
390 # divide up the span
390 # divide up the span
391 pos = 0
391 pos = 0
392 for r, l in span[2]:
392 for r, l in span[2]:
393 chunks[r] = decompress(data[pos: pos + l])
393 chunks[r] = decompress(data[pos: pos + l])
394 pos += l
394 pos += l
395
395
396 # helper to reconstruct intermediate versions
396 # helper to reconstruct intermediate versions
397 def construct(text, base, rev):
397 def construct(text, base, rev):
398 bins = [chunks[r] for r in xrange(base + 1, rev + 1)]
398 bins = [chunks[r] for r in xrange(base + 1, rev + 1)]
399 return mdiff.patches(text, bins)
399 return mdiff.patches(text, bins)
400
400
401 # build deltas
401 # build deltas
402 deltas = []
402 deltas = []
403 for d in xrange(0, len(revs) - 1):
403 for d in xrange(0, len(revs) - 1):
404 a, b = revs[d], revs[d + 1]
404 a, b = revs[d], revs[d + 1]
405 n = self.node(b)
405 n = self.node(b)
406
406
407 # do we need to construct a new delta?
407 # do we need to construct a new delta?
408 if a + 1 != b or self.base(b) == b:
408 if a + 1 != b or self.base(b) == b:
409 if a >= 0:
409 if a >= 0:
410 base = self.base(a)
410 base = self.base(a)
411 ta = chunks[self.base(a)]
411 ta = chunks[self.base(a)]
412 ta = construct(ta, base, a)
412 ta = construct(ta, base, a)
413 else:
413 else:
414 ta = ""
414 ta = ""
415
415
416 base = self.base(b)
416 base = self.base(b)
417 if a > base:
417 if a > base:
418 base = a
418 base = a
419 tb = ta
419 tb = ta
420 else:
420 else:
421 tb = chunks[self.base(b)]
421 tb = chunks[self.base(b)]
422 tb = construct(tb, base, b)
422 tb = construct(tb, base, b)
423 d = self.diff(ta, tb)
423 d = self.diff(ta, tb)
424 else:
424 else:
425 d = chunks[b]
425 d = chunks[b]
426
426
427 p = self.parents(n)
427 p = self.parents(n)
428 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
428 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
429 l = struct.pack(">l", len(meta) + len(d) + 4)
429 l = struct.pack(">l", len(meta) + len(d) + 4)
430 yield l
430 yield l
431 yield meta
431 yield meta
432 yield d
432 yield d
433
433
434 yield struct.pack(">l", 0)
434 yield struct.pack(">l", 0)
435
435
436 def addgroup(self, revs, linkmapper, transaction, unique = 0):
436 def addgroup(self, revs, linkmapper, transaction, unique = 0):
437 # given a set of deltas, add them to the revision log. the
437 # given a set of deltas, add them to the revision log. the
438 # first delta is against its parent, which should be in our
438 # first delta is against its parent, which should be in our
439 # log, the rest are against the previous delta.
439 # log, the rest are against the previous delta.
440
440
441 # track the base of the current delta log
441 # track the base of the current delta log
442 r = self.count()
442 r = self.count()
443 t = r - 1
443 t = r - 1
444 node = nullid
444 node = nullid
445
445
446 base = prev = -1
446 base = prev = -1
447 start = end = 0
447 start = end = 0
448 if r:
448 if r:
449 start = self.start(self.base(t))
449 start = self.start(self.base(t))
450 end = self.end(t)
450 end = self.end(t)
451 measure = self.length(self.base(t))
451 measure = self.length(self.base(t))
452 base = self.base(t)
452 base = self.base(t)
453 prev = self.tip()
453 prev = self.tip()
454
454
455 transaction.add(self.datafile, end)
455 transaction.add(self.datafile, end)
456 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
456 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
457 dfh = self.opener(self.datafile, "a")
457 dfh = self.opener(self.datafile, "a")
458 ifh = self.opener(self.indexfile, "a")
458 ifh = self.opener(self.indexfile, "a")
459
459
460 # loop through our set of deltas
460 # loop through our set of deltas
461 chain = None
461 chain = None
462 for chunk in revs:
462 for chunk in revs:
463 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
463 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
464 link = linkmapper(cs)
464 link = linkmapper(cs)
465 if node in self.nodemap:
465 if node in self.nodemap:
466 # this can happen if two branches make the same change
466 # this can happen if two branches make the same change
467 if unique:
467 if unique:
468 raise "already have %s" % hex(node[:4])
468 raise "already have %s" % hex(node[:4])
469 continue
469 continue
470 delta = chunk[80:]
470 delta = chunk[80:]
471
471
472 if not chain:
472 if not chain:
473 # retrieve the parent revision of the delta chain
473 # retrieve the parent revision of the delta chain
474 chain = p1
474 chain = p1
475 if not chain in self.nodemap:
475 if not chain in self.nodemap:
476 raise "unknown base %s" % short(chain[:4])
476 raise "unknown base %s" % short(chain[:4])
477
477
478 # full versions are inserted when the needed deltas become
478 # full versions are inserted when the needed deltas become
479 # comparable to the uncompressed text or when the previous
479 # comparable to the uncompressed text or when the previous
480 # version is not the one we have a delta against. We use
480 # version is not the one we have a delta against. We use
481 # the size of the previous full rev as a proxy for the
481 # the size of the previous full rev as a proxy for the
482 # current size.
482 # current size.
483
483
484 if chain == prev:
484 if chain == prev:
485 cdelta = compress(delta)
485 cdelta = compress(delta)
486
486
487 if chain != prev or (end - start + len(cdelta)) > measure * 2:
487 if chain != prev or (end - start + len(cdelta)) > measure * 2:
488 # flush our writes here so we can read it in revision
488 # flush our writes here so we can read it in revision
489 dfh.flush()
489 dfh.flush()
490 ifh.flush()
490 ifh.flush()
491 text = self.revision(chain)
491 text = self.revision(chain)
492 text = self.patches(text, [delta])
492 text = self.patches(text, [delta])
493 chk = self.addrevision(text, transaction, link, p1, p2)
493 chk = self.addrevision(text, transaction, link, p1, p2)
494 if chk != node:
494 if chk != node:
495 raise "consistency error adding group"
495 raise "consistency error adding group"
496 measure = len(text)
496 measure = len(text)
497 else:
497 else:
498 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
498 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
499 self.index.append(e)
499 self.index.append(e)
500 self.nodemap[node] = r
500 self.nodemap[node] = r
501 dfh.write(cdelta)
501 dfh.write(cdelta)
502 ifh.write(struct.pack(indexformat, *e))
502 ifh.write(struct.pack(indexformat, *e))
503
503
504 t, r, chain, prev = r, r + 1, node, node
504 t, r, chain, prev = r, r + 1, node, node
505 start = self.start(self.base(t))
505 start = self.start(self.base(t))
506 end = self.end(t)
506 end = self.end(t)
507
507
508 dfh.close()
508 dfh.close()
509 ifh.close()
509 ifh.close()
510 return node
510 return node
General Comments 0
You need to be logged in to leave comments. Login now