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