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