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