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