##// END OF EJS Templates
revlog: add revlogio interface...
Matt Mackall -
r4972:8d0cf46e default
parent child Browse files
Show More
@@ -1,1297 +1,1335 b''
1 """
1 """
2 revlog.py - storage back-end for mercurial
2 revlog.py - storage back-end for mercurial
3
3
4 This provides efficient delta storage with O(1) retrieve and append
4 This provides efficient delta storage with O(1) retrieve and append
5 and O(changes) merge between branches
5 and O(changes) merge between branches
6
6
7 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
7 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
11 """
11 """
12
12
13 from node import *
13 from node import *
14 from i18n import _
14 from i18n import _
15 import binascii, changegroup, errno, ancestor, mdiff, os
15 import binascii, changegroup, errno, ancestor, mdiff, os
16 import sha, struct, util, zlib
16 import sha, struct, util, zlib
17
17
18 # revlog version strings
18 # revlog version strings
19 REVLOGV0 = 0
19 REVLOGV0 = 0
20 REVLOGNG = 1
20 REVLOGNG = 1
21
21
22 # revlog flags
22 # revlog flags
23 REVLOGNGINLINEDATA = (1 << 16)
23 REVLOGNGINLINEDATA = (1 << 16)
24 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
24 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
25
25
26 REVLOG_DEFAULT_FORMAT = REVLOGNG
26 REVLOG_DEFAULT_FORMAT = REVLOGNG
27 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
27 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
28
28
29 def hash(text, p1, p2):
29 def hash(text, p1, p2):
30 """generate a hash from the given text and its parent hashes
30 """generate a hash from the given text and its parent hashes
31
31
32 This hash combines both the current file contents and its history
32 This hash combines both the current file contents and its history
33 in a manner that makes it easy to distinguish nodes with the same
33 in a manner that makes it easy to distinguish nodes with the same
34 content in the revision graph.
34 content in the revision graph.
35 """
35 """
36 l = [p1, p2]
36 l = [p1, p2]
37 l.sort()
37 l.sort()
38 s = sha.new(l[0])
38 s = sha.new(l[0])
39 s.update(l[1])
39 s.update(l[1])
40 s.update(text)
40 s.update(text)
41 return s.digest()
41 return s.digest()
42
42
43 def compress(text):
43 def compress(text):
44 """ generate a possibly-compressed representation of text """
44 """ generate a possibly-compressed representation of text """
45 if not text: return ("", text)
45 if not text: return ("", text)
46 if len(text) < 44:
46 if len(text) < 44:
47 if text[0] == '\0': return ("", text)
47 if text[0] == '\0': return ("", text)
48 return ('u', text)
48 return ('u', text)
49 bin = zlib.compress(text)
49 bin = zlib.compress(text)
50 if len(bin) > len(text):
50 if len(bin) > len(text):
51 if text[0] == '\0': return ("", text)
51 if text[0] == '\0': return ("", text)
52 return ('u', text)
52 return ('u', text)
53 return ("", bin)
53 return ("", bin)
54
54
55 def decompress(bin):
55 def decompress(bin):
56 """ decompress the given input """
56 """ decompress the given input """
57 if not bin: return bin
57 if not bin: return bin
58 t = bin[0]
58 t = bin[0]
59 if t == '\0': return bin
59 if t == '\0': return bin
60 if t == 'x': return zlib.decompress(bin)
60 if t == 'x': return zlib.decompress(bin)
61 if t == 'u': return bin[1:]
61 if t == 'u': return bin[1:]
62 raise RevlogError(_("unknown compression type %r") % t)
62 raise RevlogError(_("unknown compression type %r") % t)
63
63
64 indexformatv0 = ">4l20s20s20s"
64 indexformatv0 = ">4l20s20s20s"
65 v0shaoffset = 56
65 v0shaoffset = 56
66 # index ng:
66 # index ng:
67 # 6 bytes offset
67 # 6 bytes offset
68 # 2 bytes flags
68 # 2 bytes flags
69 # 4 bytes compressed length
69 # 4 bytes compressed length
70 # 4 bytes uncompressed length
70 # 4 bytes uncompressed length
71 # 4 bytes: base rev
71 # 4 bytes: base rev
72 # 4 bytes link rev
72 # 4 bytes link rev
73 # 4 bytes parent 1 rev
73 # 4 bytes parent 1 rev
74 # 4 bytes parent 2 rev
74 # 4 bytes parent 2 rev
75 # 32 bytes: nodeid
75 # 32 bytes: nodeid
76 indexformatng = ">Qiiiiii20s12x"
76 indexformatng = ">Qiiiiii20s12x"
77 ngshaoffset = 32
77 ngshaoffset = 32
78 versionformat = ">I"
78 versionformat = ">I"
79
79
80 class lazyparser(object):
80 class lazyparser(object):
81 """
81 """
82 this class avoids the need to parse the entirety of large indices
82 this class avoids the need to parse the entirety of large indices
83 """
83 """
84
84
85 # lazyparser is not safe to use on windows if win32 extensions not
85 # lazyparser is not safe to use on windows if win32 extensions not
86 # available. it keeps file handle open, which make it not possible
86 # available. it keeps file handle open, which make it not possible
87 # to break hardlinks on local cloned repos.
87 # to break hardlinks on local cloned repos.
88 safe_to_use = os.name != 'nt' or (not util.is_win_9x() and
88 safe_to_use = os.name != 'nt' or (not util.is_win_9x() and
89 hasattr(util, 'win32api'))
89 hasattr(util, 'win32api'))
90
90
91 def __init__(self, dataf, size, indexformat, shaoffset):
91 def __init__(self, dataf, size, indexformat, shaoffset):
92 self.dataf = dataf
92 self.dataf = dataf
93 self.format = indexformat
93 self.format = indexformat
94 self.s = struct.calcsize(indexformat)
94 self.s = struct.calcsize(indexformat)
95 self.indexformat = indexformat
95 self.indexformat = indexformat
96 self.datasize = size
96 self.datasize = size
97 self.l = size/self.s
97 self.l = size/self.s
98 self.index = [None] * self.l
98 self.index = [None] * self.l
99 self.map = {nullid: nullrev}
99 self.map = {nullid: nullrev}
100 self.allmap = 0
100 self.allmap = 0
101 self.all = 0
101 self.all = 0
102 self.mapfind_count = 0
102 self.mapfind_count = 0
103 self.shaoffset = shaoffset
103 self.shaoffset = shaoffset
104
104
105 def loadmap(self):
105 def loadmap(self):
106 """
106 """
107 during a commit, we need to make sure the rev being added is
107 during a commit, we need to make sure the rev being added is
108 not a duplicate. This requires loading the entire index,
108 not a duplicate. This requires loading the entire index,
109 which is fairly slow. loadmap can load up just the node map,
109 which is fairly slow. loadmap can load up just the node map,
110 which takes much less time.
110 which takes much less time.
111 """
111 """
112 if self.allmap: return
112 if self.allmap: return
113 end = self.datasize
113 end = self.datasize
114 self.allmap = 1
114 self.allmap = 1
115 cur = 0
115 cur = 0
116 count = 0
116 count = 0
117 blocksize = self.s * 256
117 blocksize = self.s * 256
118 self.dataf.seek(0)
118 self.dataf.seek(0)
119 while cur < end:
119 while cur < end:
120 data = self.dataf.read(blocksize)
120 data = self.dataf.read(blocksize)
121 off = 0
121 off = 0
122 for x in xrange(256):
122 for x in xrange(256):
123 n = data[off + self.shaoffset:off + self.shaoffset + 20]
123 n = data[off + self.shaoffset:off + self.shaoffset + 20]
124 self.map[n] = count
124 self.map[n] = count
125 count += 1
125 count += 1
126 if count >= self.l:
126 if count >= self.l:
127 break
127 break
128 off += self.s
128 off += self.s
129 cur += blocksize
129 cur += blocksize
130
130
131 def loadblock(self, blockstart, blocksize, data=None):
131 def loadblock(self, blockstart, blocksize, data=None):
132 if self.all: return
132 if self.all: return
133 if data is None:
133 if data is None:
134 self.dataf.seek(blockstart)
134 self.dataf.seek(blockstart)
135 if blockstart + blocksize > self.datasize:
135 if blockstart + blocksize > self.datasize:
136 # the revlog may have grown since we've started running,
136 # the revlog may have grown since we've started running,
137 # but we don't have space in self.index for more entries.
137 # but we don't have space in self.index for more entries.
138 # limit blocksize so that we don't get too much data.
138 # limit blocksize so that we don't get too much data.
139 blocksize = max(self.datasize - blockstart, 0)
139 blocksize = max(self.datasize - blockstart, 0)
140 data = self.dataf.read(blocksize)
140 data = self.dataf.read(blocksize)
141 lend = len(data) / self.s
141 lend = len(data) / self.s
142 i = blockstart / self.s
142 i = blockstart / self.s
143 off = 0
143 off = 0
144 # lazyindex supports __delitem__
144 # lazyindex supports __delitem__
145 if lend > len(self.index) - i:
145 if lend > len(self.index) - i:
146 lend = len(self.index) - i
146 lend = len(self.index) - i
147 for x in xrange(lend):
147 for x in xrange(lend):
148 if self.index[i + x] == None:
148 if self.index[i + x] == None:
149 b = data[off : off + self.s]
149 b = data[off : off + self.s]
150 self.index[i + x] = b
150 self.index[i + x] = b
151 n = b[self.shaoffset:self.shaoffset + 20]
151 n = b[self.shaoffset:self.shaoffset + 20]
152 self.map[n] = i + x
152 self.map[n] = i + x
153 off += self.s
153 off += self.s
154
154
155 def findnode(self, node):
155 def findnode(self, node):
156 """search backwards through the index file for a specific node"""
156 """search backwards through the index file for a specific node"""
157 if self.allmap: return None
157 if self.allmap: return None
158
158
159 # hg log will cause many many searches for the manifest
159 # hg log will cause many many searches for the manifest
160 # nodes. After we get called a few times, just load the whole
160 # nodes. After we get called a few times, just load the whole
161 # thing.
161 # thing.
162 if self.mapfind_count > 8:
162 if self.mapfind_count > 8:
163 self.loadmap()
163 self.loadmap()
164 if node in self.map:
164 if node in self.map:
165 return node
165 return node
166 return None
166 return None
167 self.mapfind_count += 1
167 self.mapfind_count += 1
168 last = self.l - 1
168 last = self.l - 1
169 while self.index[last] != None:
169 while self.index[last] != None:
170 if last == 0:
170 if last == 0:
171 self.all = 1
171 self.all = 1
172 self.allmap = 1
172 self.allmap = 1
173 return None
173 return None
174 last -= 1
174 last -= 1
175 end = (last + 1) * self.s
175 end = (last + 1) * self.s
176 blocksize = self.s * 256
176 blocksize = self.s * 256
177 while end >= 0:
177 while end >= 0:
178 start = max(end - blocksize, 0)
178 start = max(end - blocksize, 0)
179 self.dataf.seek(start)
179 self.dataf.seek(start)
180 data = self.dataf.read(end - start)
180 data = self.dataf.read(end - start)
181 findend = end - start
181 findend = end - start
182 while True:
182 while True:
183 # we're searching backwards, so weh have to make sure
183 # we're searching backwards, so weh have to make sure
184 # we don't find a changeset where this node is a parent
184 # we don't find a changeset where this node is a parent
185 off = data.rfind(node, 0, findend)
185 off = data.rfind(node, 0, findend)
186 findend = off
186 findend = off
187 if off >= 0:
187 if off >= 0:
188 i = off / self.s
188 i = off / self.s
189 off = i * self.s
189 off = i * self.s
190 n = data[off + self.shaoffset:off + self.shaoffset + 20]
190 n = data[off + self.shaoffset:off + self.shaoffset + 20]
191 if n == node:
191 if n == node:
192 self.map[n] = i + start / self.s
192 self.map[n] = i + start / self.s
193 return node
193 return node
194 else:
194 else:
195 break
195 break
196 end -= blocksize
196 end -= blocksize
197 return None
197 return None
198
198
199 def loadindex(self, i=None, end=None):
199 def loadindex(self, i=None, end=None):
200 if self.all: return
200 if self.all: return
201 all = False
201 all = False
202 if i == None:
202 if i == None:
203 blockstart = 0
203 blockstart = 0
204 blocksize = (512 / self.s) * self.s
204 blocksize = (512 / self.s) * self.s
205 end = self.datasize
205 end = self.datasize
206 all = True
206 all = True
207 else:
207 else:
208 if end:
208 if end:
209 blockstart = i * self.s
209 blockstart = i * self.s
210 end = end * self.s
210 end = end * self.s
211 blocksize = end - blockstart
211 blocksize = end - blockstart
212 else:
212 else:
213 blockstart = (i & ~63) * self.s
213 blockstart = (i & ~63) * self.s
214 blocksize = self.s * 64
214 blocksize = self.s * 64
215 end = blockstart + blocksize
215 end = blockstart + blocksize
216 while blockstart < end:
216 while blockstart < end:
217 self.loadblock(blockstart, blocksize)
217 self.loadblock(blockstart, blocksize)
218 blockstart += blocksize
218 blockstart += blocksize
219 if all: self.all = True
219 if all: self.all = True
220
220
221 class lazyindex(object):
221 class lazyindex(object):
222 """a lazy version of the index array"""
222 """a lazy version of the index array"""
223 def __init__(self, parser):
223 def __init__(self, parser):
224 self.p = parser
224 self.p = parser
225 def __len__(self):
225 def __len__(self):
226 return len(self.p.index)
226 return len(self.p.index)
227 def load(self, pos):
227 def load(self, pos):
228 if pos < 0:
228 if pos < 0:
229 pos += len(self.p.index)
229 pos += len(self.p.index)
230 self.p.loadindex(pos)
230 self.p.loadindex(pos)
231 return self.p.index[pos]
231 return self.p.index[pos]
232 def __getitem__(self, pos):
232 def __getitem__(self, pos):
233 ret = self.p.index[pos] or self.load(pos)
233 ret = self.p.index[pos] or self.load(pos)
234 if isinstance(ret, str):
234 if isinstance(ret, str):
235 ret = struct.unpack(self.p.indexformat, ret)
235 ret = struct.unpack(self.p.indexformat, ret)
236 return ret
236 return ret
237 def __setitem__(self, pos, item):
237 def __setitem__(self, pos, item):
238 self.p.index[pos] = item
238 self.p.index[pos] = item
239 def __delitem__(self, pos):
239 def __delitem__(self, pos):
240 del self.p.index[pos]
240 del self.p.index[pos]
241 def append(self, e):
241 def append(self, e):
242 self.p.index.append(e)
242 self.p.index.append(e)
243
243
244 class lazymap(object):
244 class lazymap(object):
245 """a lazy version of the node map"""
245 """a lazy version of the node map"""
246 def __init__(self, parser):
246 def __init__(self, parser):
247 self.p = parser
247 self.p = parser
248 def load(self, key):
248 def load(self, key):
249 n = self.p.findnode(key)
249 n = self.p.findnode(key)
250 if n == None:
250 if n == None:
251 raise KeyError(key)
251 raise KeyError(key)
252 def __contains__(self, key):
252 def __contains__(self, key):
253 if key in self.p.map:
253 if key in self.p.map:
254 return True
254 return True
255 self.p.loadmap()
255 self.p.loadmap()
256 return key in self.p.map
256 return key in self.p.map
257 def __iter__(self):
257 def __iter__(self):
258 yield nullid
258 yield nullid
259 for i in xrange(self.p.l):
259 for i in xrange(self.p.l):
260 ret = self.p.index[i]
260 ret = self.p.index[i]
261 if not ret:
261 if not ret:
262 self.p.loadindex(i)
262 self.p.loadindex(i)
263 ret = self.p.index[i]
263 ret = self.p.index[i]
264 if isinstance(ret, str):
264 if isinstance(ret, str):
265 ret = struct.unpack(self.p.indexformat, ret)
265 ret = struct.unpack(self.p.indexformat, ret)
266 yield ret[-1]
266 yield ret[-1]
267 def __getitem__(self, key):
267 def __getitem__(self, key):
268 try:
268 try:
269 return self.p.map[key]
269 return self.p.map[key]
270 except KeyError:
270 except KeyError:
271 try:
271 try:
272 self.load(key)
272 self.load(key)
273 return self.p.map[key]
273 return self.p.map[key]
274 except KeyError:
274 except KeyError:
275 raise KeyError("node " + hex(key))
275 raise KeyError("node " + hex(key))
276 def __setitem__(self, key, val):
276 def __setitem__(self, key, val):
277 self.p.map[key] = val
277 self.p.map[key] = val
278 def __delitem__(self, key):
278 def __delitem__(self, key):
279 del self.p.map[key]
279 del self.p.map[key]
280
280
281 class RevlogError(Exception): pass
281 class RevlogError(Exception): pass
282 class LookupError(RevlogError): pass
282 class LookupError(RevlogError): pass
283
283
284 def getoffset(q):
284 def getoffset(q):
285 if q & 0xFFFF:
285 if q & 0xFFFF:
286 raise RevlogError(_('incompatible revision flag %x') % q)
286 raise RevlogError(_('incompatible revision flag %x') % q)
287 return int(q >> 16)
287 return int(q >> 16)
288
288
289 def gettype(q):
289 def gettype(q):
290 return int(q & 0xFFFF)
290 return int(q & 0xFFFF)
291
291
292 def offset_type(offset, type):
292 def offset_type(offset, type):
293 return long(long(offset) << 16 | type)
293 return long(long(offset) << 16 | type)
294
294
295 class revlogoldio(object):
296 def __init__(self):
297 self.chunkcache = None
298
299 def parseindex(self, fp, st, inline):
300 s = struct.calcsize(indexformatv0)
301 index = []
302 nodemap = {nullid: nullrev}
303 n = 0
304 leftover = None
305 while True:
306 if st:
307 data = fp.read(65536)
308 else:
309 # hack for httprangereader, it doesn't do partial reads well
310 data = fp.read()
311 if not data:
312 break
313 if leftover:
314 data = leftover + data
315 leftover = None
316 off = 0
317 l = len(data)
318 while off < l:
319 if l - off < s:
320 leftover = data[off:]
321 break
322 cur = data[off:off + s]
323 off += s
324 e = struct.unpack(indexformatv0, cur)
325 index.append(e)
326 nodemap[e[-1]] = n
327 n += 1
328 if not st:
329 break
330
331 return index, nodemap
332
333 class revlogio(object):
334 def __init__(self):
335 self.chunkcache = None
336
337 def parseindex(self, fp, st, inline):
338 if (lazyparser.safe_to_use and not inline and
339 st and st.st_size > 10000):
340 # big index, let's parse it on demand
341 parser = lazyparser(fp, st.st_size, indexformatng, ngshaoffset)
342 index = lazyindex(parser)
343 nodemap = lazymap(parser)
344 e = list(index[0])
345 type = gettype(e[0])
346 e[0] = offset_type(0, type)
347 index[0] = e
348 return index, nodemap
349
350 s = struct.calcsize(indexformatng)
351 index = []
352 nodemap = {nullid: nullrev}
353 n = 0
354 leftover = None
355 while True:
356 if st:
357 data = fp.read(65536)
358 else:
359 # hack for httprangereader, it doesn't do partial reads well
360 data = fp.read()
361 if not data:
362 break
363 if n == 0 and inline:
364 # cache the first chunk
365 self.chunkcache = (0, data)
366 if leftover:
367 data = leftover + data
368 leftover = None
369 off = 0
370 l = len(data)
371 while off < l:
372 if l - off < s:
373 leftover = data[off:]
374 break
375 cur = data[off:off + s]
376 off += s
377 e = struct.unpack(indexformatng, cur)
378 index.append(e)
379 nodemap[e[-1]] = n
380 n += 1
381 if inline:
382 if e[1] < 0:
383 break
384 off += e[1]
385 if off > l:
386 # some things don't seek well, just read it
387 fp.read(off - l)
388 break
389 if not st:
390 break
391
392 e = list(index[0])
393 type = gettype(e[0])
394 e[0] = offset_type(0, type)
395 index[0] = e
396
397 return index, nodemap
398
295 class revlog(object):
399 class revlog(object):
296 """
400 """
297 the underlying revision storage object
401 the underlying revision storage object
298
402
299 A revlog consists of two parts, an index and the revision data.
403 A revlog consists of two parts, an index and the revision data.
300
404
301 The index is a file with a fixed record size containing
405 The index is a file with a fixed record size containing
302 information on each revision, includings its nodeid (hash), the
406 information on each revision, includings its nodeid (hash), the
303 nodeids of its parents, the position and offset of its data within
407 nodeids of its parents, the position and offset of its data within
304 the data file, and the revision it's based on. Finally, each entry
408 the data file, and the revision it's based on. Finally, each entry
305 contains a linkrev entry that can serve as a pointer to external
409 contains a linkrev entry that can serve as a pointer to external
306 data.
410 data.
307
411
308 The revision data itself is a linear collection of data chunks.
412 The revision data itself is a linear collection of data chunks.
309 Each chunk represents a revision and is usually represented as a
413 Each chunk represents a revision and is usually represented as a
310 delta against the previous chunk. To bound lookup time, runs of
414 delta against the previous chunk. To bound lookup time, runs of
311 deltas are limited to about 2 times the length of the original
415 deltas are limited to about 2 times the length of the original
312 version data. This makes retrieval of a version proportional to
416 version data. This makes retrieval of a version proportional to
313 its size, or O(1) relative to the number of revisions.
417 its size, or O(1) relative to the number of revisions.
314
418
315 Both pieces of the revlog are written to in an append-only
419 Both pieces of the revlog are written to in an append-only
316 fashion, which means we never need to rewrite a file to insert or
420 fashion, which means we never need to rewrite a file to insert or
317 remove data, and can use some simple techniques to avoid the need
421 remove data, and can use some simple techniques to avoid the need
318 for locking while reading.
422 for locking while reading.
319 """
423 """
320 def __init__(self, opener, indexfile):
424 def __init__(self, opener, indexfile):
321 """
425 """
322 create a revlog object
426 create a revlog object
323
427
324 opener is a function that abstracts the file opening operation
428 opener is a function that abstracts the file opening operation
325 and can be used to implement COW semantics or the like.
429 and can be used to implement COW semantics or the like.
326 """
430 """
327 self.indexfile = indexfile
431 self.indexfile = indexfile
328 self.datafile = indexfile[:-2] + ".d"
432 self.datafile = indexfile[:-2] + ".d"
329 self.opener = opener
433 self.opener = opener
330
434
331 self.indexstat = None
435 self.indexstat = None
332 self.cache = None
436 self.cache = None
333 self.chunkcache = None
334 self.defversion = REVLOG_DEFAULT_VERSION
437 self.defversion = REVLOG_DEFAULT_VERSION
335 if hasattr(opener, "defversion"):
438 if hasattr(opener, "defversion"):
336 self.defversion = opener.defversion
439 self.defversion = opener.defversion
337 if self.defversion & REVLOGNG:
440 if self.defversion & REVLOGNG:
338 self.defversion |= REVLOGNGINLINEDATA
441 self.defversion |= REVLOGNGINLINEDATA
339 self._load()
442 self._load()
340
443
341 def _load(self):
444 def _load(self):
342 v = self.defversion
445 v = self.defversion
343 try:
446 try:
344 f = self.opener(self.indexfile)
447 f = self.opener(self.indexfile)
345 i = f.read(4)
448 i = f.read(4)
346 f.seek(0)
449 f.seek(0)
347 if len(i) > 0:
450 if len(i) > 0:
348 v = struct.unpack(versionformat, i)[0]
451 v = struct.unpack(versionformat, i)[0]
349 except IOError, inst:
452 except IOError, inst:
350 if inst.errno != errno.ENOENT:
453 if inst.errno != errno.ENOENT:
351 raise
454 raise
352 i = ""
455 i = ""
353 else:
456 else:
354 try:
457 try:
355 st = util.fstat(f)
458 st = util.fstat(f)
356 except AttributeError, inst:
459 except AttributeError, inst:
357 st = None
460 st = None
358 else:
461 else:
359 oldst = self.indexstat
462 oldst = self.indexstat
360 if (oldst and st.st_dev == oldst.st_dev
463 if (oldst and st.st_dev == oldst.st_dev
361 and st.st_ino == oldst.st_ino
464 and st.st_ino == oldst.st_ino
362 and st.st_mtime == oldst.st_mtime
465 and st.st_mtime == oldst.st_mtime
363 and st.st_ctime == oldst.st_ctime
466 and st.st_ctime == oldst.st_ctime
364 and st.st_size == oldst.st_size):
467 and st.st_size == oldst.st_size):
365 return
468 return
366 self.indexstat = st
469 self.indexstat = st
367 flags = v & ~0xFFFF
470 flags = v & ~0xFFFF
368 fmt = v & 0xFFFF
471 fmt = v & 0xFFFF
369 if fmt == REVLOGV0:
472 if fmt == REVLOGV0:
370 if flags:
473 if flags:
371 raise RevlogError(_("index %s unknown flags %#04x for format v0")
474 raise RevlogError(_("index %s unknown flags %#04x for format v0")
372 % (self.indexfile, flags >> 16))
475 % (self.indexfile, flags >> 16))
373 elif fmt == REVLOGNG:
476 elif fmt == REVLOGNG:
374 if flags & ~REVLOGNGINLINEDATA:
477 if flags & ~REVLOGNGINLINEDATA:
375 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
478 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
376 % (self.indexfile, flags >> 16))
479 % (self.indexfile, flags >> 16))
377 else:
480 else:
378 raise RevlogError(_("index %s unknown format %d")
481 raise RevlogError(_("index %s unknown format %d")
379 % (self.indexfile, fmt))
482 % (self.indexfile, fmt))
380 self.version = v
483 self.version = v
381 self.nodemap = {nullid: nullrev}
484 self.nodemap = {nullid: nullrev}
382 self.index = []
485 self.index = []
486 self._io = revlogio()
383 self.indexformat = indexformatng
487 self.indexformat = indexformatng
384 if self.version == REVLOGV0:
488 if self.version == REVLOGV0:
489 self._io = revlogoldio()
385 self.indexformat = indexformatv0
490 self.indexformat = indexformatv0
386 if i:
491 if i:
387 self._parseindex(f, st)
492 self.index, self.nodemap = self._io.parseindex(f, st, self._inline())
388
389 def _parseindex(self, fp, st):
390 shaoffset = ngshaoffset
391 if self.version == REVLOGV0:
392 shaoffset = v0shaoffset
393
394 if (lazyparser.safe_to_use and not self._inline() and
395 st and st.st_size > 10000):
396 # big index, let's parse it on demand
397 parser = lazyparser(fp, st.st_size, self.indexformat, shaoffset)
398 self.index = lazyindex(parser)
399 self.nodemap = lazymap(parser)
400 if self.version != REVLOGV0:
401 e = list(self.index[0])
402 type = gettype(e[0])
403 e[0] = offset_type(0, type)
404 self.index[0] = e
405 return
406
407 s = struct.calcsize(self.indexformat)
408 self.index = []
409 self.nodemap = {nullid: nullrev}
410 inline = self._inline()
411 n = 0
412 leftover = None
413 while True:
414 if st:
415 data = fp.read(65536)
416 else:
417 # hack for httprangereader, it doesn't do partial reads well
418 data = fp.read()
419 if not data:
420 break
421 if n == 0 and self._inline():
422 # cache the first chunk
423 self.chunkcache = (0, data)
424 if leftover:
425 data = leftover + data
426 leftover = None
427 off = 0
428 l = len(data)
429 while off < l:
430 if l - off < s:
431 leftover = data[off:]
432 break
433 cur = data[off:off + s]
434 off += s
435 e = struct.unpack(self.indexformat, cur)
436 self.index.append(e)
437 self.nodemap[e[-1]] = n
438 n += 1
439 if inline:
440 if e[1] < 0:
441 break
442 off += e[1]
443 if off > l:
444 # some things don't seek well, just read it
445 fp.read(off - l)
446 break
447 if not st:
448 break
449
450 if self.version != REVLOGV0:
451 e = list(self.index[0])
452 type = gettype(e[0])
453 e[0] = offset_type(0, type)
454 self.index[0] = e
455
493
456 def _loadindex(self, start, end):
494 def _loadindex(self, start, end):
457 """load a block of indexes all at once from the lazy parser"""
495 """load a block of indexes all at once from the lazy parser"""
458 if isinstance(self.index, lazyindex):
496 if isinstance(self.index, lazyindex):
459 self.index.p.loadindex(start, end)
497 self.index.p.loadindex(start, end)
460
498
461 def _loadindexmap(self):
499 def _loadindexmap(self):
462 """loads both the map and the index from the lazy parser"""
500 """loads both the map and the index from the lazy parser"""
463 if isinstance(self.index, lazyindex):
501 if isinstance(self.index, lazyindex):
464 p = self.index.p
502 p = self.index.p
465 p.loadindex()
503 p.loadindex()
466 self.nodemap = p.map
504 self.nodemap = p.map
467
505
468 def _loadmap(self):
506 def _loadmap(self):
469 """loads the map from the lazy parser"""
507 """loads the map from the lazy parser"""
470 if isinstance(self.nodemap, lazymap):
508 if isinstance(self.nodemap, lazymap):
471 self.nodemap.p.loadmap()
509 self.nodemap.p.loadmap()
472 self.nodemap = self.nodemap.p.map
510 self.nodemap = self.nodemap.p.map
473
511
474 def _inline(self): return self.version & REVLOGNGINLINEDATA
512 def _inline(self): return self.version & REVLOGNGINLINEDATA
475
513
476 def tip(self): return self.node(len(self.index) - 1)
514 def tip(self): return self.node(len(self.index) - 1)
477 def count(self): return len(self.index)
515 def count(self): return len(self.index)
478 def node(self, rev):
516 def node(self, rev):
479 return rev == nullrev and nullid or self.index[rev][-1]
517 return rev == nullrev and nullid or self.index[rev][-1]
480 def rev(self, node):
518 def rev(self, node):
481 try:
519 try:
482 return self.nodemap[node]
520 return self.nodemap[node]
483 except KeyError:
521 except KeyError:
484 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
522 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
485 def linkrev(self, node):
523 def linkrev(self, node):
486 return (node == nullid) and nullrev or self.index[self.rev(node)][-4]
524 return (node == nullid) and nullrev or self.index[self.rev(node)][-4]
487 def parents(self, node):
525 def parents(self, node):
488 if node == nullid: return (nullid, nullid)
526 if node == nullid: return (nullid, nullid)
489 r = self.rev(node)
527 r = self.rev(node)
490 d = self.index[r][-3:-1]
528 d = self.index[r][-3:-1]
491 if self.version == REVLOGV0:
529 if self.version == REVLOGV0:
492 return d
530 return d
493 return (self.node(d[0]), self.node(d[1]))
531 return (self.node(d[0]), self.node(d[1]))
494 def parentrevs(self, rev):
532 def parentrevs(self, rev):
495 if rev == nullrev:
533 if rev == nullrev:
496 return (nullrev, nullrev)
534 return (nullrev, nullrev)
497 d = self.index[rev][-3:-1]
535 d = self.index[rev][-3:-1]
498 if self.version == REVLOGV0:
536 if self.version == REVLOGV0:
499 return (self.rev(d[0]), self.rev(d[1]))
537 return (self.rev(d[0]), self.rev(d[1]))
500 return d
538 return d
501 def start(self, rev):
539 def start(self, rev):
502 if rev == nullrev:
540 if rev == nullrev:
503 return 0
541 return 0
504 if self.version != REVLOGV0:
542 if self.version != REVLOGV0:
505 return getoffset(self.index[rev][0])
543 return getoffset(self.index[rev][0])
506 return self.index[rev][0]
544 return self.index[rev][0]
507
545
508 def end(self, rev): return self.start(rev) + self.length(rev)
546 def end(self, rev): return self.start(rev) + self.length(rev)
509
547
510 def size(self, rev):
548 def size(self, rev):
511 """return the length of the uncompressed text for a given revision"""
549 """return the length of the uncompressed text for a given revision"""
512 if rev == nullrev:
550 if rev == nullrev:
513 return 0
551 return 0
514 l = -1
552 l = -1
515 if self.version != REVLOGV0:
553 if self.version != REVLOGV0:
516 l = self.index[rev][2]
554 l = self.index[rev][2]
517 if l >= 0:
555 if l >= 0:
518 return l
556 return l
519
557
520 t = self.revision(self.node(rev))
558 t = self.revision(self.node(rev))
521 return len(t)
559 return len(t)
522
560
523 # alternate implementation, The advantage to this code is it
561 # alternate implementation, The advantage to this code is it
524 # will be faster for a single revision. But, the results are not
562 # will be faster for a single revision. But, the results are not
525 # cached, so finding the size of every revision will be slower.
563 # cached, so finding the size of every revision will be slower.
526 """
564 """
527 if self.cache and self.cache[1] == rev:
565 if self.cache and self.cache[1] == rev:
528 return len(self.cache[2])
566 return len(self.cache[2])
529
567
530 base = self.base(rev)
568 base = self.base(rev)
531 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
569 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
532 base = self.cache[1]
570 base = self.cache[1]
533 text = self.cache[2]
571 text = self.cache[2]
534 else:
572 else:
535 text = self.revision(self.node(base))
573 text = self.revision(self.node(base))
536
574
537 l = len(text)
575 l = len(text)
538 for x in xrange(base + 1, rev + 1):
576 for x in xrange(base + 1, rev + 1):
539 l = mdiff.patchedsize(l, self.chunk(x))
577 l = mdiff.patchedsize(l, self.chunk(x))
540 return l
578 return l
541 """
579 """
542
580
543 def length(self, rev):
581 def length(self, rev):
544 if rev == nullrev:
582 if rev == nullrev:
545 return 0
583 return 0
546 else:
584 else:
547 return self.index[rev][1]
585 return self.index[rev][1]
548 def base(self, rev):
586 def base(self, rev):
549 if (rev == nullrev):
587 if (rev == nullrev):
550 return nullrev
588 return nullrev
551 else:
589 else:
552 return self.index[rev][-5]
590 return self.index[rev][-5]
553
591
554 def reachable(self, node, stop=None):
592 def reachable(self, node, stop=None):
555 """return a hash of all nodes ancestral to a given node, including
593 """return a hash of all nodes ancestral to a given node, including
556 the node itself, stopping when stop is matched"""
594 the node itself, stopping when stop is matched"""
557 reachable = {}
595 reachable = {}
558 visit = [node]
596 visit = [node]
559 reachable[node] = 1
597 reachable[node] = 1
560 if stop:
598 if stop:
561 stopn = self.rev(stop)
599 stopn = self.rev(stop)
562 else:
600 else:
563 stopn = 0
601 stopn = 0
564 while visit:
602 while visit:
565 n = visit.pop(0)
603 n = visit.pop(0)
566 if n == stop:
604 if n == stop:
567 continue
605 continue
568 if n == nullid:
606 if n == nullid:
569 continue
607 continue
570 for p in self.parents(n):
608 for p in self.parents(n):
571 if self.rev(p) < stopn:
609 if self.rev(p) < stopn:
572 continue
610 continue
573 if p not in reachable:
611 if p not in reachable:
574 reachable[p] = 1
612 reachable[p] = 1
575 visit.append(p)
613 visit.append(p)
576 return reachable
614 return reachable
577
615
578 def nodesbetween(self, roots=None, heads=None):
616 def nodesbetween(self, roots=None, heads=None):
579 """Return a tuple containing three elements. Elements 1 and 2 contain
617 """Return a tuple containing three elements. Elements 1 and 2 contain
580 a final list bases and heads after all the unreachable ones have been
618 a final list bases and heads after all the unreachable ones have been
581 pruned. Element 0 contains a topologically sorted list of all
619 pruned. Element 0 contains a topologically sorted list of all
582
620
583 nodes that satisfy these constraints:
621 nodes that satisfy these constraints:
584 1. All nodes must be descended from a node in roots (the nodes on
622 1. All nodes must be descended from a node in roots (the nodes on
585 roots are considered descended from themselves).
623 roots are considered descended from themselves).
586 2. All nodes must also be ancestors of a node in heads (the nodes in
624 2. All nodes must also be ancestors of a node in heads (the nodes in
587 heads are considered to be their own ancestors).
625 heads are considered to be their own ancestors).
588
626
589 If roots is unspecified, nullid is assumed as the only root.
627 If roots is unspecified, nullid is assumed as the only root.
590 If heads is unspecified, it is taken to be the output of the
628 If heads is unspecified, it is taken to be the output of the
591 heads method (i.e. a list of all nodes in the repository that
629 heads method (i.e. a list of all nodes in the repository that
592 have no children)."""
630 have no children)."""
593 nonodes = ([], [], [])
631 nonodes = ([], [], [])
594 if roots is not None:
632 if roots is not None:
595 roots = list(roots)
633 roots = list(roots)
596 if not roots:
634 if not roots:
597 return nonodes
635 return nonodes
598 lowestrev = min([self.rev(n) for n in roots])
636 lowestrev = min([self.rev(n) for n in roots])
599 else:
637 else:
600 roots = [nullid] # Everybody's a descendent of nullid
638 roots = [nullid] # Everybody's a descendent of nullid
601 lowestrev = nullrev
639 lowestrev = nullrev
602 if (lowestrev == nullrev) and (heads is None):
640 if (lowestrev == nullrev) and (heads is None):
603 # We want _all_ the nodes!
641 # We want _all_ the nodes!
604 return ([self.node(r) for r in xrange(0, self.count())],
642 return ([self.node(r) for r in xrange(0, self.count())],
605 [nullid], list(self.heads()))
643 [nullid], list(self.heads()))
606 if heads is None:
644 if heads is None:
607 # All nodes are ancestors, so the latest ancestor is the last
645 # All nodes are ancestors, so the latest ancestor is the last
608 # node.
646 # node.
609 highestrev = self.count() - 1
647 highestrev = self.count() - 1
610 # Set ancestors to None to signal that every node is an ancestor.
648 # Set ancestors to None to signal that every node is an ancestor.
611 ancestors = None
649 ancestors = None
612 # Set heads to an empty dictionary for later discovery of heads
650 # Set heads to an empty dictionary for later discovery of heads
613 heads = {}
651 heads = {}
614 else:
652 else:
615 heads = list(heads)
653 heads = list(heads)
616 if not heads:
654 if not heads:
617 return nonodes
655 return nonodes
618 ancestors = {}
656 ancestors = {}
619 # Turn heads into a dictionary so we can remove 'fake' heads.
657 # Turn heads into a dictionary so we can remove 'fake' heads.
620 # Also, later we will be using it to filter out the heads we can't
658 # Also, later we will be using it to filter out the heads we can't
621 # find from roots.
659 # find from roots.
622 heads = dict.fromkeys(heads, 0)
660 heads = dict.fromkeys(heads, 0)
623 # Start at the top and keep marking parents until we're done.
661 # Start at the top and keep marking parents until we're done.
624 nodestotag = heads.keys()
662 nodestotag = heads.keys()
625 # Remember where the top was so we can use it as a limit later.
663 # Remember where the top was so we can use it as a limit later.
626 highestrev = max([self.rev(n) for n in nodestotag])
664 highestrev = max([self.rev(n) for n in nodestotag])
627 while nodestotag:
665 while nodestotag:
628 # grab a node to tag
666 # grab a node to tag
629 n = nodestotag.pop()
667 n = nodestotag.pop()
630 # Never tag nullid
668 # Never tag nullid
631 if n == nullid:
669 if n == nullid:
632 continue
670 continue
633 # A node's revision number represents its place in a
671 # A node's revision number represents its place in a
634 # topologically sorted list of nodes.
672 # topologically sorted list of nodes.
635 r = self.rev(n)
673 r = self.rev(n)
636 if r >= lowestrev:
674 if r >= lowestrev:
637 if n not in ancestors:
675 if n not in ancestors:
638 # If we are possibly a descendent of one of the roots
676 # If we are possibly a descendent of one of the roots
639 # and we haven't already been marked as an ancestor
677 # and we haven't already been marked as an ancestor
640 ancestors[n] = 1 # Mark as ancestor
678 ancestors[n] = 1 # Mark as ancestor
641 # Add non-nullid parents to list of nodes to tag.
679 # Add non-nullid parents to list of nodes to tag.
642 nodestotag.extend([p for p in self.parents(n) if
680 nodestotag.extend([p for p in self.parents(n) if
643 p != nullid])
681 p != nullid])
644 elif n in heads: # We've seen it before, is it a fake head?
682 elif n in heads: # We've seen it before, is it a fake head?
645 # So it is, real heads should not be the ancestors of
683 # So it is, real heads should not be the ancestors of
646 # any other heads.
684 # any other heads.
647 heads.pop(n)
685 heads.pop(n)
648 if not ancestors:
686 if not ancestors:
649 return nonodes
687 return nonodes
650 # Now that we have our set of ancestors, we want to remove any
688 # Now that we have our set of ancestors, we want to remove any
651 # roots that are not ancestors.
689 # roots that are not ancestors.
652
690
653 # If one of the roots was nullid, everything is included anyway.
691 # If one of the roots was nullid, everything is included anyway.
654 if lowestrev > nullrev:
692 if lowestrev > nullrev:
655 # But, since we weren't, let's recompute the lowest rev to not
693 # But, since we weren't, let's recompute the lowest rev to not
656 # include roots that aren't ancestors.
694 # include roots that aren't ancestors.
657
695
658 # Filter out roots that aren't ancestors of heads
696 # Filter out roots that aren't ancestors of heads
659 roots = [n for n in roots if n in ancestors]
697 roots = [n for n in roots if n in ancestors]
660 # Recompute the lowest revision
698 # Recompute the lowest revision
661 if roots:
699 if roots:
662 lowestrev = min([self.rev(n) for n in roots])
700 lowestrev = min([self.rev(n) for n in roots])
663 else:
701 else:
664 # No more roots? Return empty list
702 # No more roots? Return empty list
665 return nonodes
703 return nonodes
666 else:
704 else:
667 # We are descending from nullid, and don't need to care about
705 # We are descending from nullid, and don't need to care about
668 # any other roots.
706 # any other roots.
669 lowestrev = nullrev
707 lowestrev = nullrev
670 roots = [nullid]
708 roots = [nullid]
671 # Transform our roots list into a 'set' (i.e. a dictionary where the
709 # Transform our roots list into a 'set' (i.e. a dictionary where the
672 # values don't matter.
710 # values don't matter.
673 descendents = dict.fromkeys(roots, 1)
711 descendents = dict.fromkeys(roots, 1)
674 # Also, keep the original roots so we can filter out roots that aren't
712 # Also, keep the original roots so we can filter out roots that aren't
675 # 'real' roots (i.e. are descended from other roots).
713 # 'real' roots (i.e. are descended from other roots).
676 roots = descendents.copy()
714 roots = descendents.copy()
677 # Our topologically sorted list of output nodes.
715 # Our topologically sorted list of output nodes.
678 orderedout = []
716 orderedout = []
679 # Don't start at nullid since we don't want nullid in our output list,
717 # Don't start at nullid since we don't want nullid in our output list,
680 # and if nullid shows up in descedents, empty parents will look like
718 # and if nullid shows up in descedents, empty parents will look like
681 # they're descendents.
719 # they're descendents.
682 for r in xrange(max(lowestrev, 0), highestrev + 1):
720 for r in xrange(max(lowestrev, 0), highestrev + 1):
683 n = self.node(r)
721 n = self.node(r)
684 isdescendent = False
722 isdescendent = False
685 if lowestrev == nullrev: # Everybody is a descendent of nullid
723 if lowestrev == nullrev: # Everybody is a descendent of nullid
686 isdescendent = True
724 isdescendent = True
687 elif n in descendents:
725 elif n in descendents:
688 # n is already a descendent
726 # n is already a descendent
689 isdescendent = True
727 isdescendent = True
690 # This check only needs to be done here because all the roots
728 # This check only needs to be done here because all the roots
691 # will start being marked is descendents before the loop.
729 # will start being marked is descendents before the loop.
692 if n in roots:
730 if n in roots:
693 # If n was a root, check if it's a 'real' root.
731 # If n was a root, check if it's a 'real' root.
694 p = tuple(self.parents(n))
732 p = tuple(self.parents(n))
695 # If any of its parents are descendents, it's not a root.
733 # If any of its parents are descendents, it's not a root.
696 if (p[0] in descendents) or (p[1] in descendents):
734 if (p[0] in descendents) or (p[1] in descendents):
697 roots.pop(n)
735 roots.pop(n)
698 else:
736 else:
699 p = tuple(self.parents(n))
737 p = tuple(self.parents(n))
700 # A node is a descendent if either of its parents are
738 # A node is a descendent if either of its parents are
701 # descendents. (We seeded the dependents list with the roots
739 # descendents. (We seeded the dependents list with the roots
702 # up there, remember?)
740 # up there, remember?)
703 if (p[0] in descendents) or (p[1] in descendents):
741 if (p[0] in descendents) or (p[1] in descendents):
704 descendents[n] = 1
742 descendents[n] = 1
705 isdescendent = True
743 isdescendent = True
706 if isdescendent and ((ancestors is None) or (n in ancestors)):
744 if isdescendent and ((ancestors is None) or (n in ancestors)):
707 # Only include nodes that are both descendents and ancestors.
745 # Only include nodes that are both descendents and ancestors.
708 orderedout.append(n)
746 orderedout.append(n)
709 if (ancestors is not None) and (n in heads):
747 if (ancestors is not None) and (n in heads):
710 # We're trying to figure out which heads are reachable
748 # We're trying to figure out which heads are reachable
711 # from roots.
749 # from roots.
712 # Mark this head as having been reached
750 # Mark this head as having been reached
713 heads[n] = 1
751 heads[n] = 1
714 elif ancestors is None:
752 elif ancestors is None:
715 # Otherwise, we're trying to discover the heads.
753 # Otherwise, we're trying to discover the heads.
716 # Assume this is a head because if it isn't, the next step
754 # Assume this is a head because if it isn't, the next step
717 # will eventually remove it.
755 # will eventually remove it.
718 heads[n] = 1
756 heads[n] = 1
719 # But, obviously its parents aren't.
757 # But, obviously its parents aren't.
720 for p in self.parents(n):
758 for p in self.parents(n):
721 heads.pop(p, None)
759 heads.pop(p, None)
722 heads = [n for n in heads.iterkeys() if heads[n] != 0]
760 heads = [n for n in heads.iterkeys() if heads[n] != 0]
723 roots = roots.keys()
761 roots = roots.keys()
724 assert orderedout
762 assert orderedout
725 assert roots
763 assert roots
726 assert heads
764 assert heads
727 return (orderedout, roots, heads)
765 return (orderedout, roots, heads)
728
766
729 def heads(self, start=None, stop=None):
767 def heads(self, start=None, stop=None):
730 """return the list of all nodes that have no children
768 """return the list of all nodes that have no children
731
769
732 if start is specified, only heads that are descendants of
770 if start is specified, only heads that are descendants of
733 start will be returned
771 start will be returned
734 if stop is specified, it will consider all the revs from stop
772 if stop is specified, it will consider all the revs from stop
735 as if they had no children
773 as if they had no children
736 """
774 """
737 if start is None:
775 if start is None:
738 start = nullid
776 start = nullid
739 if stop is None:
777 if stop is None:
740 stop = []
778 stop = []
741 stoprevs = dict.fromkeys([self.rev(n) for n in stop])
779 stoprevs = dict.fromkeys([self.rev(n) for n in stop])
742 startrev = self.rev(start)
780 startrev = self.rev(start)
743 reachable = {startrev: 1}
781 reachable = {startrev: 1}
744 heads = {startrev: 1}
782 heads = {startrev: 1}
745
783
746 parentrevs = self.parentrevs
784 parentrevs = self.parentrevs
747 for r in xrange(startrev + 1, self.count()):
785 for r in xrange(startrev + 1, self.count()):
748 for p in parentrevs(r):
786 for p in parentrevs(r):
749 if p in reachable:
787 if p in reachable:
750 if r not in stoprevs:
788 if r not in stoprevs:
751 reachable[r] = 1
789 reachable[r] = 1
752 heads[r] = 1
790 heads[r] = 1
753 if p in heads and p not in stoprevs:
791 if p in heads and p not in stoprevs:
754 del heads[p]
792 del heads[p]
755
793
756 return [self.node(r) for r in heads]
794 return [self.node(r) for r in heads]
757
795
758 def children(self, node):
796 def children(self, node):
759 """find the children of a given node"""
797 """find the children of a given node"""
760 c = []
798 c = []
761 p = self.rev(node)
799 p = self.rev(node)
762 for r in range(p + 1, self.count()):
800 for r in range(p + 1, self.count()):
763 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
801 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
764 if prevs:
802 if prevs:
765 for pr in prevs:
803 for pr in prevs:
766 if pr == p:
804 if pr == p:
767 c.append(self.node(r))
805 c.append(self.node(r))
768 elif p == nullrev:
806 elif p == nullrev:
769 c.append(self.node(r))
807 c.append(self.node(r))
770 return c
808 return c
771
809
772 def _match(self, id):
810 def _match(self, id):
773 if isinstance(id, (long, int)):
811 if isinstance(id, (long, int)):
774 # rev
812 # rev
775 return self.node(id)
813 return self.node(id)
776 if len(id) == 20:
814 if len(id) == 20:
777 # possibly a binary node
815 # possibly a binary node
778 # odds of a binary node being all hex in ASCII are 1 in 10**25
816 # odds of a binary node being all hex in ASCII are 1 in 10**25
779 try:
817 try:
780 node = id
818 node = id
781 r = self.rev(node) # quick search the index
819 r = self.rev(node) # quick search the index
782 return node
820 return node
783 except LookupError:
821 except LookupError:
784 pass # may be partial hex id
822 pass # may be partial hex id
785 try:
823 try:
786 # str(rev)
824 # str(rev)
787 rev = int(id)
825 rev = int(id)
788 if str(rev) != id: raise ValueError
826 if str(rev) != id: raise ValueError
789 if rev < 0: rev = self.count() + rev
827 if rev < 0: rev = self.count() + rev
790 if rev < 0 or rev >= self.count(): raise ValueError
828 if rev < 0 or rev >= self.count(): raise ValueError
791 return self.node(rev)
829 return self.node(rev)
792 except (ValueError, OverflowError):
830 except (ValueError, OverflowError):
793 pass
831 pass
794 if len(id) == 40:
832 if len(id) == 40:
795 try:
833 try:
796 # a full hex nodeid?
834 # a full hex nodeid?
797 node = bin(id)
835 node = bin(id)
798 r = self.rev(node)
836 r = self.rev(node)
799 return node
837 return node
800 except TypeError:
838 except TypeError:
801 pass
839 pass
802
840
803 def _partialmatch(self, id):
841 def _partialmatch(self, id):
804 if len(id) < 40:
842 if len(id) < 40:
805 try:
843 try:
806 # hex(node)[:...]
844 # hex(node)[:...]
807 bin_id = bin(id[:len(id) & ~1]) # grab an even number of digits
845 bin_id = bin(id[:len(id) & ~1]) # grab an even number of digits
808 node = None
846 node = None
809 for n in self.nodemap:
847 for n in self.nodemap:
810 if n.startswith(bin_id) and hex(n).startswith(id):
848 if n.startswith(bin_id) and hex(n).startswith(id):
811 if node is not None:
849 if node is not None:
812 raise LookupError(_("Ambiguous identifier"))
850 raise LookupError(_("Ambiguous identifier"))
813 node = n
851 node = n
814 if node is not None:
852 if node is not None:
815 return node
853 return node
816 except TypeError:
854 except TypeError:
817 pass
855 pass
818
856
819 def lookup(self, id):
857 def lookup(self, id):
820 """locate a node based on:
858 """locate a node based on:
821 - revision number or str(revision number)
859 - revision number or str(revision number)
822 - nodeid or subset of hex nodeid
860 - nodeid or subset of hex nodeid
823 """
861 """
824
862
825 n = self._match(id)
863 n = self._match(id)
826 if n is not None:
864 if n is not None:
827 return n
865 return n
828 n = self._partialmatch(id)
866 n = self._partialmatch(id)
829 if n:
867 if n:
830 return n
868 return n
831
869
832 raise LookupError(_("No match found"))
870 raise LookupError(_("No match found"))
833
871
834 def cmp(self, node, text):
872 def cmp(self, node, text):
835 """compare text with a given file revision"""
873 """compare text with a given file revision"""
836 p1, p2 = self.parents(node)
874 p1, p2 = self.parents(node)
837 return hash(text, p1, p2) != node
875 return hash(text, p1, p2) != node
838
876
839 def diff(self, a, b):
877 def diff(self, a, b):
840 """return a delta between two revisions"""
878 """return a delta between two revisions"""
841 return mdiff.textdiff(a, b)
879 return mdiff.textdiff(a, b)
842
880
843 def patches(self, t, pl):
881 def patches(self, t, pl):
844 """apply a list of patches to a string"""
882 """apply a list of patches to a string"""
845 return mdiff.patches(t, pl)
883 return mdiff.patches(t, pl)
846
884
847 def chunk(self, rev, df=None, cachelen=4096):
885 def chunk(self, rev, df=None, cachelen=4096):
848 start, length = self.start(rev), self.length(rev)
886 start, length = self.start(rev), self.length(rev)
849 inline = self._inline()
887 inline = self._inline()
850 if inline:
888 if inline:
851 start += (rev + 1) * struct.calcsize(self.indexformat)
889 start += (rev + 1) * struct.calcsize(self.indexformat)
852 end = start + length
890 end = start + length
853 def loadcache(df):
891 def loadcache(df):
854 cache_length = max(cachelen, length) # 4k
892 cache_length = max(cachelen, length) # 4k
855 if not df:
893 if not df:
856 if inline:
894 if inline:
857 df = self.opener(self.indexfile)
895 df = self.opener(self.indexfile)
858 else:
896 else:
859 df = self.opener(self.datafile)
897 df = self.opener(self.datafile)
860 df.seek(start)
898 df.seek(start)
861 self.chunkcache = (start, df.read(cache_length))
899 self._io.chunkcache = (start, df.read(cache_length))
862
900
863 if not self.chunkcache:
901 if not self._io.chunkcache:
864 loadcache(df)
902 loadcache(df)
865
903
866 cache_start = self.chunkcache[0]
904 cache_start = self._io.chunkcache[0]
867 cache_end = cache_start + len(self.chunkcache[1])
905 cache_end = cache_start + len(self._io.chunkcache[1])
868 if start >= cache_start and end <= cache_end:
906 if start >= cache_start and end <= cache_end:
869 # it is cached
907 # it is cached
870 offset = start - cache_start
908 offset = start - cache_start
871 else:
909 else:
872 loadcache(df)
910 loadcache(df)
873 offset = 0
911 offset = 0
874
912
875 #def checkchunk():
913 #def checkchunk():
876 # df = self.opener(self.datafile)
914 # df = self.opener(self.datafile)
877 # df.seek(start)
915 # df.seek(start)
878 # return df.read(length)
916 # return df.read(length)
879 #assert s == checkchunk()
917 #assert s == checkchunk()
880 return decompress(self.chunkcache[1][offset:offset + length])
918 return decompress(self._io.chunkcache[1][offset:offset + length])
881
919
882 def delta(self, node):
920 def delta(self, node):
883 """return or calculate a delta between a node and its predecessor"""
921 """return or calculate a delta between a node and its predecessor"""
884 r = self.rev(node)
922 r = self.rev(node)
885 return self.revdiff(r - 1, r)
923 return self.revdiff(r - 1, r)
886
924
887 def revdiff(self, rev1, rev2):
925 def revdiff(self, rev1, rev2):
888 """return or calculate a delta between two revisions"""
926 """return or calculate a delta between two revisions"""
889 b1 = self.base(rev1)
927 b1 = self.base(rev1)
890 b2 = self.base(rev2)
928 b2 = self.base(rev2)
891 if b1 == b2 and rev1 + 1 == rev2:
929 if b1 == b2 and rev1 + 1 == rev2:
892 return self.chunk(rev2)
930 return self.chunk(rev2)
893 else:
931 else:
894 return self.diff(self.revision(self.node(rev1)),
932 return self.diff(self.revision(self.node(rev1)),
895 self.revision(self.node(rev2)))
933 self.revision(self.node(rev2)))
896
934
897 def revision(self, node):
935 def revision(self, node):
898 """return an uncompressed revision of a given"""
936 """return an uncompressed revision of a given"""
899 if node == nullid: return ""
937 if node == nullid: return ""
900 if self.cache and self.cache[0] == node: return self.cache[2]
938 if self.cache and self.cache[0] == node: return self.cache[2]
901
939
902 # look up what we need to read
940 # look up what we need to read
903 text = None
941 text = None
904 rev = self.rev(node)
942 rev = self.rev(node)
905 base = self.base(rev)
943 base = self.base(rev)
906
944
907 if self._inline():
945 if self._inline():
908 # we probably have the whole chunk cached
946 # we probably have the whole chunk cached
909 df = None
947 df = None
910 else:
948 else:
911 df = self.opener(self.datafile)
949 df = self.opener(self.datafile)
912
950
913 # do we have useful data cached?
951 # do we have useful data cached?
914 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
952 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
915 base = self.cache[1]
953 base = self.cache[1]
916 text = self.cache[2]
954 text = self.cache[2]
917 self._loadindex(base, rev + 1)
955 self._loadindex(base, rev + 1)
918 else:
956 else:
919 self._loadindex(base, rev + 1)
957 self._loadindex(base, rev + 1)
920 text = self.chunk(base, df=df)
958 text = self.chunk(base, df=df)
921
959
922 bins = []
960 bins = []
923 for r in xrange(base + 1, rev + 1):
961 for r in xrange(base + 1, rev + 1):
924 bins.append(self.chunk(r, df=df))
962 bins.append(self.chunk(r, df=df))
925
963
926 text = self.patches(text, bins)
964 text = self.patches(text, bins)
927
965
928 p1, p2 = self.parents(node)
966 p1, p2 = self.parents(node)
929 if node != hash(text, p1, p2):
967 if node != hash(text, p1, p2):
930 raise RevlogError(_("integrity check failed on %s:%d")
968 raise RevlogError(_("integrity check failed on %s:%d")
931 % (self.datafile, rev))
969 % (self.datafile, rev))
932
970
933 self.cache = (node, rev, text)
971 self.cache = (node, rev, text)
934 return text
972 return text
935
973
936 def checkinlinesize(self, tr, fp=None):
974 def checkinlinesize(self, tr, fp=None):
937 if not self._inline():
975 if not self._inline():
938 return
976 return
939 if not fp:
977 if not fp:
940 fp = self.opener(self.indexfile, 'r')
978 fp = self.opener(self.indexfile, 'r')
941 fp.seek(0, 2)
979 fp.seek(0, 2)
942 size = fp.tell()
980 size = fp.tell()
943 if size < 131072:
981 if size < 131072:
944 return
982 return
945 trinfo = tr.find(self.indexfile)
983 trinfo = tr.find(self.indexfile)
946 if trinfo == None:
984 if trinfo == None:
947 raise RevlogError(_("%s not found in the transaction")
985 raise RevlogError(_("%s not found in the transaction")
948 % self.indexfile)
986 % self.indexfile)
949
987
950 trindex = trinfo[2]
988 trindex = trinfo[2]
951 dataoff = self.start(trindex)
989 dataoff = self.start(trindex)
952
990
953 tr.add(self.datafile, dataoff)
991 tr.add(self.datafile, dataoff)
954 df = self.opener(self.datafile, 'w')
992 df = self.opener(self.datafile, 'w')
955 calc = struct.calcsize(self.indexformat)
993 calc = struct.calcsize(self.indexformat)
956 for r in xrange(self.count()):
994 for r in xrange(self.count()):
957 start = self.start(r) + (r + 1) * calc
995 start = self.start(r) + (r + 1) * calc
958 length = self.length(r)
996 length = self.length(r)
959 fp.seek(start)
997 fp.seek(start)
960 d = fp.read(length)
998 d = fp.read(length)
961 df.write(d)
999 df.write(d)
962 fp.close()
1000 fp.close()
963 df.close()
1001 df.close()
964 fp = self.opener(self.indexfile, 'w', atomictemp=True)
1002 fp = self.opener(self.indexfile, 'w', atomictemp=True)
965 self.version &= ~(REVLOGNGINLINEDATA)
1003 self.version &= ~(REVLOGNGINLINEDATA)
966 if self.count():
1004 if self.count():
967 x = self.index[0]
1005 x = self.index[0]
968 e = struct.pack(self.indexformat, *x)[4:]
1006 e = struct.pack(self.indexformat, *x)[4:]
969 l = struct.pack(versionformat, self.version)
1007 l = struct.pack(versionformat, self.version)
970 fp.write(l)
1008 fp.write(l)
971 fp.write(e)
1009 fp.write(e)
972
1010
973 for i in xrange(1, self.count()):
1011 for i in xrange(1, self.count()):
974 x = self.index[i]
1012 x = self.index[i]
975 e = struct.pack(self.indexformat, *x)
1013 e = struct.pack(self.indexformat, *x)
976 fp.write(e)
1014 fp.write(e)
977
1015
978 # if we don't call rename, the temp file will never replace the
1016 # if we don't call rename, the temp file will never replace the
979 # real index
1017 # real index
980 fp.rename()
1018 fp.rename()
981
1019
982 tr.replace(self.indexfile, trindex * calc)
1020 tr.replace(self.indexfile, trindex * calc)
983 self.chunkcache = None
1021 self._io.chunkcache = None
984
1022
985 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
1023 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
986 """add a revision to the log
1024 """add a revision to the log
987
1025
988 text - the revision data to add
1026 text - the revision data to add
989 transaction - the transaction object used for rollback
1027 transaction - the transaction object used for rollback
990 link - the linkrev data to add
1028 link - the linkrev data to add
991 p1, p2 - the parent nodeids of the revision
1029 p1, p2 - the parent nodeids of the revision
992 d - an optional precomputed delta
1030 d - an optional precomputed delta
993 """
1031 """
994 if not self._inline():
1032 if not self._inline():
995 dfh = self.opener(self.datafile, "a")
1033 dfh = self.opener(self.datafile, "a")
996 else:
1034 else:
997 dfh = None
1035 dfh = None
998 ifh = self.opener(self.indexfile, "a+")
1036 ifh = self.opener(self.indexfile, "a+")
999 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
1037 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
1000
1038
1001 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
1039 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
1002 if text is None: text = ""
1040 if text is None: text = ""
1003 if p1 is None: p1 = self.tip()
1041 if p1 is None: p1 = self.tip()
1004 if p2 is None: p2 = nullid
1042 if p2 is None: p2 = nullid
1005
1043
1006 node = hash(text, p1, p2)
1044 node = hash(text, p1, p2)
1007
1045
1008 if node in self.nodemap:
1046 if node in self.nodemap:
1009 return node
1047 return node
1010
1048
1011 n = self.count()
1049 n = self.count()
1012 t = n - 1
1050 t = n - 1
1013
1051
1014 if n:
1052 if n:
1015 base = self.base(t)
1053 base = self.base(t)
1016 start = self.start(base)
1054 start = self.start(base)
1017 end = self.end(t)
1055 end = self.end(t)
1018 if not d:
1056 if not d:
1019 prev = self.revision(self.tip())
1057 prev = self.revision(self.tip())
1020 d = self.diff(prev, text)
1058 d = self.diff(prev, text)
1021 data = compress(d)
1059 data = compress(d)
1022 l = len(data[1]) + len(data[0])
1060 l = len(data[1]) + len(data[0])
1023 dist = end - start + l
1061 dist = end - start + l
1024
1062
1025 # full versions are inserted when the needed deltas
1063 # full versions are inserted when the needed deltas
1026 # become comparable to the uncompressed text
1064 # become comparable to the uncompressed text
1027 if not n or dist > len(text) * 2:
1065 if not n or dist > len(text) * 2:
1028 data = compress(text)
1066 data = compress(text)
1029 l = len(data[1]) + len(data[0])
1067 l = len(data[1]) + len(data[0])
1030 base = n
1068 base = n
1031 else:
1069 else:
1032 base = self.base(t)
1070 base = self.base(t)
1033
1071
1034 offset = 0
1072 offset = 0
1035 if t >= 0:
1073 if t >= 0:
1036 offset = self.end(t)
1074 offset = self.end(t)
1037
1075
1038 if self.version == REVLOGV0:
1076 if self.version == REVLOGV0:
1039 e = (offset, l, base, link, p1, p2, node)
1077 e = (offset, l, base, link, p1, p2, node)
1040 else:
1078 else:
1041 e = (offset_type(offset, 0), l, len(text),
1079 e = (offset_type(offset, 0), l, len(text),
1042 base, link, self.rev(p1), self.rev(p2), node)
1080 base, link, self.rev(p1), self.rev(p2), node)
1043
1081
1044 self.index.append(e)
1082 self.index.append(e)
1045 self.nodemap[node] = n
1083 self.nodemap[node] = n
1046 entry = struct.pack(self.indexformat, *e)
1084 entry = struct.pack(self.indexformat, *e)
1047
1085
1048 if not self._inline():
1086 if not self._inline():
1049 transaction.add(self.datafile, offset)
1087 transaction.add(self.datafile, offset)
1050 transaction.add(self.indexfile, n * len(entry))
1088 transaction.add(self.indexfile, n * len(entry))
1051 if data[0]:
1089 if data[0]:
1052 dfh.write(data[0])
1090 dfh.write(data[0])
1053 dfh.write(data[1])
1091 dfh.write(data[1])
1054 dfh.flush()
1092 dfh.flush()
1055 else:
1093 else:
1056 ifh.seek(0, 2)
1094 ifh.seek(0, 2)
1057 transaction.add(self.indexfile, ifh.tell(), self.count() - 1)
1095 transaction.add(self.indexfile, ifh.tell(), self.count() - 1)
1058
1096
1059 if len(self.index) == 1 and self.version != REVLOGV0:
1097 if len(self.index) == 1 and self.version != REVLOGV0:
1060 l = struct.pack(versionformat, self.version)
1098 l = struct.pack(versionformat, self.version)
1061 ifh.write(l)
1099 ifh.write(l)
1062 entry = entry[4:]
1100 entry = entry[4:]
1063
1101
1064 ifh.write(entry)
1102 ifh.write(entry)
1065
1103
1066 if self._inline():
1104 if self._inline():
1067 ifh.write(data[0])
1105 ifh.write(data[0])
1068 ifh.write(data[1])
1106 ifh.write(data[1])
1069 self.checkinlinesize(transaction, ifh)
1107 self.checkinlinesize(transaction, ifh)
1070
1108
1071 self.cache = (node, n, text)
1109 self.cache = (node, n, text)
1072 return node
1110 return node
1073
1111
1074 def ancestor(self, a, b):
1112 def ancestor(self, a, b):
1075 """calculate the least common ancestor of nodes a and b"""
1113 """calculate the least common ancestor of nodes a and b"""
1076
1114
1077 def parents(rev):
1115 def parents(rev):
1078 return [p for p in self.parentrevs(rev) if p != nullrev]
1116 return [p for p in self.parentrevs(rev) if p != nullrev]
1079
1117
1080 c = ancestor.ancestor(self.rev(a), self.rev(b), parents)
1118 c = ancestor.ancestor(self.rev(a), self.rev(b), parents)
1081 if c is None:
1119 if c is None:
1082 return nullid
1120 return nullid
1083
1121
1084 return self.node(c)
1122 return self.node(c)
1085
1123
1086 def group(self, nodelist, lookup, infocollect=None):
1124 def group(self, nodelist, lookup, infocollect=None):
1087 """calculate a delta group
1125 """calculate a delta group
1088
1126
1089 Given a list of changeset revs, return a set of deltas and
1127 Given a list of changeset revs, return a set of deltas and
1090 metadata corresponding to nodes. the first delta is
1128 metadata corresponding to nodes. the first delta is
1091 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
1129 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
1092 have this parent as it has all history before these
1130 have this parent as it has all history before these
1093 changesets. parent is parent[0]
1131 changesets. parent is parent[0]
1094 """
1132 """
1095 revs = [self.rev(n) for n in nodelist]
1133 revs = [self.rev(n) for n in nodelist]
1096
1134
1097 # if we don't have any revisions touched by these changesets, bail
1135 # if we don't have any revisions touched by these changesets, bail
1098 if not revs:
1136 if not revs:
1099 yield changegroup.closechunk()
1137 yield changegroup.closechunk()
1100 return
1138 return
1101
1139
1102 # add the parent of the first rev
1140 # add the parent of the first rev
1103 p = self.parents(self.node(revs[0]))[0]
1141 p = self.parents(self.node(revs[0]))[0]
1104 revs.insert(0, self.rev(p))
1142 revs.insert(0, self.rev(p))
1105
1143
1106 # build deltas
1144 # build deltas
1107 for d in xrange(0, len(revs) - 1):
1145 for d in xrange(0, len(revs) - 1):
1108 a, b = revs[d], revs[d + 1]
1146 a, b = revs[d], revs[d + 1]
1109 nb = self.node(b)
1147 nb = self.node(b)
1110
1148
1111 if infocollect is not None:
1149 if infocollect is not None:
1112 infocollect(nb)
1150 infocollect(nb)
1113
1151
1114 d = self.revdiff(a, b)
1152 d = self.revdiff(a, b)
1115 p = self.parents(nb)
1153 p = self.parents(nb)
1116 meta = nb + p[0] + p[1] + lookup(nb)
1154 meta = nb + p[0] + p[1] + lookup(nb)
1117 yield changegroup.genchunk("%s%s" % (meta, d))
1155 yield changegroup.genchunk("%s%s" % (meta, d))
1118
1156
1119 yield changegroup.closechunk()
1157 yield changegroup.closechunk()
1120
1158
1121 def addgroup(self, revs, linkmapper, transaction, unique=0):
1159 def addgroup(self, revs, linkmapper, transaction, unique=0):
1122 """
1160 """
1123 add a delta group
1161 add a delta group
1124
1162
1125 given a set of deltas, add them to the revision log. the
1163 given a set of deltas, add them to the revision log. the
1126 first delta is against its parent, which should be in our
1164 first delta is against its parent, which should be in our
1127 log, the rest are against the previous delta.
1165 log, the rest are against the previous delta.
1128 """
1166 """
1129
1167
1130 #track the base of the current delta log
1168 #track the base of the current delta log
1131 r = self.count()
1169 r = self.count()
1132 t = r - 1
1170 t = r - 1
1133 node = None
1171 node = None
1134
1172
1135 base = prev = nullrev
1173 base = prev = nullrev
1136 start = end = textlen = 0
1174 start = end = textlen = 0
1137 if r:
1175 if r:
1138 end = self.end(t)
1176 end = self.end(t)
1139
1177
1140 ifh = self.opener(self.indexfile, "a+")
1178 ifh = self.opener(self.indexfile, "a+")
1141 ifh.seek(0, 2)
1179 ifh.seek(0, 2)
1142 transaction.add(self.indexfile, ifh.tell(), self.count())
1180 transaction.add(self.indexfile, ifh.tell(), self.count())
1143 if self._inline():
1181 if self._inline():
1144 dfh = None
1182 dfh = None
1145 else:
1183 else:
1146 transaction.add(self.datafile, end)
1184 transaction.add(self.datafile, end)
1147 dfh = self.opener(self.datafile, "a")
1185 dfh = self.opener(self.datafile, "a")
1148
1186
1149 # loop through our set of deltas
1187 # loop through our set of deltas
1150 chain = None
1188 chain = None
1151 for chunk in revs:
1189 for chunk in revs:
1152 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
1190 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
1153 link = linkmapper(cs)
1191 link = linkmapper(cs)
1154 if node in self.nodemap:
1192 if node in self.nodemap:
1155 # this can happen if two branches make the same change
1193 # this can happen if two branches make the same change
1156 # if unique:
1194 # if unique:
1157 # raise RevlogError(_("already have %s") % hex(node[:4]))
1195 # raise RevlogError(_("already have %s") % hex(node[:4]))
1158 chain = node
1196 chain = node
1159 continue
1197 continue
1160 delta = chunk[80:]
1198 delta = chunk[80:]
1161
1199
1162 for p in (p1, p2):
1200 for p in (p1, p2):
1163 if not p in self.nodemap:
1201 if not p in self.nodemap:
1164 raise LookupError(_("unknown parent %s") % short(p))
1202 raise LookupError(_("unknown parent %s") % short(p))
1165
1203
1166 if not chain:
1204 if not chain:
1167 # retrieve the parent revision of the delta chain
1205 # retrieve the parent revision of the delta chain
1168 chain = p1
1206 chain = p1
1169 if not chain in self.nodemap:
1207 if not chain in self.nodemap:
1170 raise LookupError(_("unknown base %s") % short(chain[:4]))
1208 raise LookupError(_("unknown base %s") % short(chain[:4]))
1171
1209
1172 # full versions are inserted when the needed deltas become
1210 # full versions are inserted when the needed deltas become
1173 # comparable to the uncompressed text or when the previous
1211 # comparable to the uncompressed text or when the previous
1174 # version is not the one we have a delta against. We use
1212 # version is not the one we have a delta against. We use
1175 # the size of the previous full rev as a proxy for the
1213 # the size of the previous full rev as a proxy for the
1176 # current size.
1214 # current size.
1177
1215
1178 if chain == prev:
1216 if chain == prev:
1179 tempd = compress(delta)
1217 tempd = compress(delta)
1180 cdelta = tempd[0] + tempd[1]
1218 cdelta = tempd[0] + tempd[1]
1181 textlen = mdiff.patchedsize(textlen, delta)
1219 textlen = mdiff.patchedsize(textlen, delta)
1182
1220
1183 if chain != prev or (end - start + len(cdelta)) > textlen * 2:
1221 if chain != prev or (end - start + len(cdelta)) > textlen * 2:
1184 # flush our writes here so we can read it in revision
1222 # flush our writes here so we can read it in revision
1185 if dfh:
1223 if dfh:
1186 dfh.flush()
1224 dfh.flush()
1187 ifh.flush()
1225 ifh.flush()
1188 text = self.revision(chain)
1226 text = self.revision(chain)
1189 text = self.patches(text, [delta])
1227 text = self.patches(text, [delta])
1190 chk = self._addrevision(text, transaction, link, p1, p2, None,
1228 chk = self._addrevision(text, transaction, link, p1, p2, None,
1191 ifh, dfh)
1229 ifh, dfh)
1192 if not dfh and not self._inline():
1230 if not dfh and not self._inline():
1193 # addrevision switched from inline to conventional
1231 # addrevision switched from inline to conventional
1194 # reopen the index
1232 # reopen the index
1195 dfh = self.opener(self.datafile, "a")
1233 dfh = self.opener(self.datafile, "a")
1196 ifh = self.opener(self.indexfile, "a")
1234 ifh = self.opener(self.indexfile, "a")
1197 if chk != node:
1235 if chk != node:
1198 raise RevlogError(_("consistency error adding group"))
1236 raise RevlogError(_("consistency error adding group"))
1199 textlen = len(text)
1237 textlen = len(text)
1200 else:
1238 else:
1201 if self.version == REVLOGV0:
1239 if self.version == REVLOGV0:
1202 e = (end, len(cdelta), base, link, p1, p2, node)
1240 e = (end, len(cdelta), base, link, p1, p2, node)
1203 else:
1241 else:
1204 e = (offset_type(end, 0), len(cdelta), textlen, base,
1242 e = (offset_type(end, 0), len(cdelta), textlen, base,
1205 link, self.rev(p1), self.rev(p2), node)
1243 link, self.rev(p1), self.rev(p2), node)
1206 self.index.append(e)
1244 self.index.append(e)
1207 self.nodemap[node] = r
1245 self.nodemap[node] = r
1208 if self._inline():
1246 if self._inline():
1209 ifh.write(struct.pack(self.indexformat, *e))
1247 ifh.write(struct.pack(self.indexformat, *e))
1210 ifh.write(cdelta)
1248 ifh.write(cdelta)
1211 self.checkinlinesize(transaction, ifh)
1249 self.checkinlinesize(transaction, ifh)
1212 if not self._inline():
1250 if not self._inline():
1213 dfh = self.opener(self.datafile, "a")
1251 dfh = self.opener(self.datafile, "a")
1214 ifh = self.opener(self.indexfile, "a")
1252 ifh = self.opener(self.indexfile, "a")
1215 else:
1253 else:
1216 dfh.write(cdelta)
1254 dfh.write(cdelta)
1217 ifh.write(struct.pack(self.indexformat, *e))
1255 ifh.write(struct.pack(self.indexformat, *e))
1218
1256
1219 t, r, chain, prev = r, r + 1, node, node
1257 t, r, chain, prev = r, r + 1, node, node
1220 base = self.base(t)
1258 base = self.base(t)
1221 start = self.start(base)
1259 start = self.start(base)
1222 end = self.end(t)
1260 end = self.end(t)
1223
1261
1224 return node
1262 return node
1225
1263
1226 def strip(self, rev, minlink):
1264 def strip(self, rev, minlink):
1227 if self.count() == 0 or rev >= self.count():
1265 if self.count() == 0 or rev >= self.count():
1228 return
1266 return
1229
1267
1230 if isinstance(self.index, lazyindex):
1268 if isinstance(self.index, lazyindex):
1231 self._loadindexmap()
1269 self._loadindexmap()
1232
1270
1233 # When stripping away a revision, we need to make sure it
1271 # When stripping away a revision, we need to make sure it
1234 # does not actually belong to an older changeset.
1272 # does not actually belong to an older changeset.
1235 # The minlink parameter defines the oldest revision
1273 # The minlink parameter defines the oldest revision
1236 # we're allowed to strip away.
1274 # we're allowed to strip away.
1237 while minlink > self.index[rev][-4]:
1275 while minlink > self.index[rev][-4]:
1238 rev += 1
1276 rev += 1
1239 if rev >= self.count():
1277 if rev >= self.count():
1240 return
1278 return
1241
1279
1242 # first truncate the files on disk
1280 # first truncate the files on disk
1243 end = self.start(rev)
1281 end = self.start(rev)
1244 if not self._inline():
1282 if not self._inline():
1245 df = self.opener(self.datafile, "a")
1283 df = self.opener(self.datafile, "a")
1246 df.truncate(end)
1284 df.truncate(end)
1247 end = rev * struct.calcsize(self.indexformat)
1285 end = rev * struct.calcsize(self.indexformat)
1248 else:
1286 else:
1249 end += rev * struct.calcsize(self.indexformat)
1287 end += rev * struct.calcsize(self.indexformat)
1250
1288
1251 indexf = self.opener(self.indexfile, "a")
1289 indexf = self.opener(self.indexfile, "a")
1252 indexf.truncate(end)
1290 indexf.truncate(end)
1253
1291
1254 # then reset internal state in memory to forget those revisions
1292 # then reset internal state in memory to forget those revisions
1255 self.cache = None
1293 self.cache = None
1256 self.chunkcache = None
1294 self._io.chunkcache = None
1257 for x in xrange(rev, self.count()):
1295 for x in xrange(rev, self.count()):
1258 del self.nodemap[self.node(x)]
1296 del self.nodemap[self.node(x)]
1259
1297
1260 del self.index[rev:]
1298 del self.index[rev:]
1261
1299
1262 def checksize(self):
1300 def checksize(self):
1263 expected = 0
1301 expected = 0
1264 if self.count():
1302 if self.count():
1265 expected = self.end(self.count() - 1)
1303 expected = self.end(self.count() - 1)
1266
1304
1267 try:
1305 try:
1268 f = self.opener(self.datafile)
1306 f = self.opener(self.datafile)
1269 f.seek(0, 2)
1307 f.seek(0, 2)
1270 actual = f.tell()
1308 actual = f.tell()
1271 dd = actual - expected
1309 dd = actual - expected
1272 except IOError, inst:
1310 except IOError, inst:
1273 if inst.errno != errno.ENOENT:
1311 if inst.errno != errno.ENOENT:
1274 raise
1312 raise
1275 dd = 0
1313 dd = 0
1276
1314
1277 try:
1315 try:
1278 f = self.opener(self.indexfile)
1316 f = self.opener(self.indexfile)
1279 f.seek(0, 2)
1317 f.seek(0, 2)
1280 actual = f.tell()
1318 actual = f.tell()
1281 s = struct.calcsize(self.indexformat)
1319 s = struct.calcsize(self.indexformat)
1282 i = actual / s
1320 i = actual / s
1283 di = actual - (i * s)
1321 di = actual - (i * s)
1284 if self._inline():
1322 if self._inline():
1285 databytes = 0
1323 databytes = 0
1286 for r in xrange(self.count()):
1324 for r in xrange(self.count()):
1287 databytes += self.length(r)
1325 databytes += self.length(r)
1288 dd = 0
1326 dd = 0
1289 di = actual - self.count() * s - databytes
1327 di = actual - self.count() * s - databytes
1290 except IOError, inst:
1328 except IOError, inst:
1291 if inst.errno != errno.ENOENT:
1329 if inst.errno != errno.ENOENT:
1292 raise
1330 raise
1293 di = 0
1331 di = 0
1294
1332
1295 return (dd, di)
1333 return (dd, di)
1296
1334
1297
1335
General Comments 0
You need to be logged in to leave comments. Login now