##// END OF EJS Templates
revlog: simplify the v1 immediate parser...
Matt Mackall -
r4975:8b7e480a default
parent child Browse files
Show More
@@ -1,1318 +1,1296 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):
295 class revlogoldio(object):
296 def __init__(self):
296 def __init__(self):
297 self.chunkcache = None
297 self.chunkcache = None
298
298
299 def parseindex(self, fp, st, inline):
299 def parseindex(self, fp, st, inline):
300 s = struct.calcsize(indexformatv0)
300 s = struct.calcsize(indexformatv0)
301 index = []
301 index = []
302 nodemap = {nullid: nullrev}
302 nodemap = {nullid: nullrev}
303 n = off = 0
303 n = off = 0
304 data = fp.read()
304 data = fp.read()
305 l = len(data)
305 l = len(data)
306 while off + s <= l:
306 while off + s <= l:
307 cur = data[off:off + s]
307 cur = data[off:off + s]
308 off += s
308 off += s
309 e = struct.unpack(indexformatv0, cur)
309 e = struct.unpack(indexformatv0, cur)
310 index.append(e)
310 index.append(e)
311 nodemap[e[-1]] = n
311 nodemap[e[-1]] = n
312 n += 1
312 n += 1
313
313
314 return index, nodemap
314 return index, nodemap
315
315
316 class revlogio(object):
316 class revlogio(object):
317 def __init__(self):
317 def __init__(self):
318 self.chunkcache = None
318 self.chunkcache = None
319
319
320 def parseindex(self, fp, st, inline):
320 def parseindex(self, fp, st, inline):
321 if (lazyparser.safe_to_use and not inline and
321 if (lazyparser.safe_to_use and not inline and
322 st and st.st_size > 1000000):
322 st and st.st_size > 1000000):
323 # big index, let's parse it on demand
323 # big index, let's parse it on demand
324 parser = lazyparser(fp, st.st_size, indexformatng, ngshaoffset)
324 parser = lazyparser(fp, st.st_size, indexformatng, ngshaoffset)
325 index = lazyindex(parser)
325 index = lazyindex(parser)
326 nodemap = lazymap(parser)
326 nodemap = lazymap(parser)
327 e = list(index[0])
327 e = list(index[0])
328 type = gettype(e[0])
328 type = gettype(e[0])
329 e[0] = offset_type(0, type)
329 e[0] = offset_type(0, type)
330 index[0] = e
330 index[0] = e
331 return index, nodemap
331 return index, nodemap
332
332
333 s = struct.calcsize(indexformatng)
333 s = struct.calcsize(indexformatng)
334 index = []
334 index = []
335 nodemap = {nullid: nullrev}
335 nodemap = {nullid: nullrev}
336 n = 0
336 n = off = 0
337 leftover = None
337 # if we're not using lazymap, always read the whole index
338 while True:
338 data = fp.read()
339 if st:
339 l = len(data)
340 data = fp.read(65536)
340 if inline:
341 else:
341 self.chunkcache = (0, data)
342 # hack for httprangereader, it doesn't do partial reads well
342 while off + s <= l:
343 data = fp.read()
343 e = struct.unpack(indexformatng, data[off:off + s])
344 if not data:
344 index.append(e)
345 break
345 nodemap[e[-1]] = n
346 if n == 0 and inline:
346 n += 1
347 # cache the first chunk
347 off += s
348 self.chunkcache = (0, data)
348 if inline:
349 if leftover:
349 if e[1] < 0:
350 data = leftover + data
351 leftover = None
352 off = 0
353 l = len(data)
354 while off < l:
355 if l - off < s:
356 leftover = data[off:]
357 break
350 break
358 cur = data[off:off + s]
351 off += e[1]
359 off += s
360 e = struct.unpack(indexformatng, cur)
361 index.append(e)
362 nodemap[e[-1]] = n
363 n += 1
364 if inline:
365 if e[1] < 0:
366 break
367 off += e[1]
368 if off > l:
369 # some things don't seek well, just read it
370 fp.read(off - l)
371 break
372 if not st:
373 break
374
352
375 e = list(index[0])
353 e = list(index[0])
376 type = gettype(e[0])
354 type = gettype(e[0])
377 e[0] = offset_type(0, type)
355 e[0] = offset_type(0, type)
378 index[0] = e
356 index[0] = e
379
357
380 return index, nodemap
358 return index, nodemap
381
359
382 class revlog(object):
360 class revlog(object):
383 """
361 """
384 the underlying revision storage object
362 the underlying revision storage object
385
363
386 A revlog consists of two parts, an index and the revision data.
364 A revlog consists of two parts, an index and the revision data.
387
365
388 The index is a file with a fixed record size containing
366 The index is a file with a fixed record size containing
389 information on each revision, includings its nodeid (hash), the
367 information on each revision, includings its nodeid (hash), the
390 nodeids of its parents, the position and offset of its data within
368 nodeids of its parents, the position and offset of its data within
391 the data file, and the revision it's based on. Finally, each entry
369 the data file, and the revision it's based on. Finally, each entry
392 contains a linkrev entry that can serve as a pointer to external
370 contains a linkrev entry that can serve as a pointer to external
393 data.
371 data.
394
372
395 The revision data itself is a linear collection of data chunks.
373 The revision data itself is a linear collection of data chunks.
396 Each chunk represents a revision and is usually represented as a
374 Each chunk represents a revision and is usually represented as a
397 delta against the previous chunk. To bound lookup time, runs of
375 delta against the previous chunk. To bound lookup time, runs of
398 deltas are limited to about 2 times the length of the original
376 deltas are limited to about 2 times the length of the original
399 version data. This makes retrieval of a version proportional to
377 version data. This makes retrieval of a version proportional to
400 its size, or O(1) relative to the number of revisions.
378 its size, or O(1) relative to the number of revisions.
401
379
402 Both pieces of the revlog are written to in an append-only
380 Both pieces of the revlog are written to in an append-only
403 fashion, which means we never need to rewrite a file to insert or
381 fashion, which means we never need to rewrite a file to insert or
404 remove data, and can use some simple techniques to avoid the need
382 remove data, and can use some simple techniques to avoid the need
405 for locking while reading.
383 for locking while reading.
406 """
384 """
407 def __init__(self, opener, indexfile):
385 def __init__(self, opener, indexfile):
408 """
386 """
409 create a revlog object
387 create a revlog object
410
388
411 opener is a function that abstracts the file opening operation
389 opener is a function that abstracts the file opening operation
412 and can be used to implement COW semantics or the like.
390 and can be used to implement COW semantics or the like.
413 """
391 """
414 self.indexfile = indexfile
392 self.indexfile = indexfile
415 self.datafile = indexfile[:-2] + ".d"
393 self.datafile = indexfile[:-2] + ".d"
416 self.opener = opener
394 self.opener = opener
417
395
418 self.indexstat = None
396 self.indexstat = None
419 self.cache = None
397 self.cache = None
420 self.defversion = REVLOG_DEFAULT_VERSION
398 self.defversion = REVLOG_DEFAULT_VERSION
421 if hasattr(opener, "defversion"):
399 if hasattr(opener, "defversion"):
422 self.defversion = opener.defversion
400 self.defversion = opener.defversion
423 if self.defversion & REVLOGNG:
401 if self.defversion & REVLOGNG:
424 self.defversion |= REVLOGNGINLINEDATA
402 self.defversion |= REVLOGNGINLINEDATA
425 self._load()
403 self._load()
426
404
427 def _load(self):
405 def _load(self):
428 v = self.defversion
406 v = self.defversion
429 try:
407 try:
430 f = self.opener(self.indexfile)
408 f = self.opener(self.indexfile)
431 i = f.read(4)
409 i = f.read(4)
432 f.seek(0)
410 f.seek(0)
433 if len(i) > 0:
411 if len(i) > 0:
434 v = struct.unpack(versionformat, i)[0]
412 v = struct.unpack(versionformat, i)[0]
435 except IOError, inst:
413 except IOError, inst:
436 if inst.errno != errno.ENOENT:
414 if inst.errno != errno.ENOENT:
437 raise
415 raise
438 i = ""
416 i = ""
439 else:
417 else:
440 try:
418 try:
441 st = util.fstat(f)
419 st = util.fstat(f)
442 except AttributeError, inst:
420 except AttributeError, inst:
443 st = None
421 st = None
444 else:
422 else:
445 oldst = self.indexstat
423 oldst = self.indexstat
446 if (oldst and st.st_dev == oldst.st_dev
424 if (oldst and st.st_dev == oldst.st_dev
447 and st.st_ino == oldst.st_ino
425 and st.st_ino == oldst.st_ino
448 and st.st_mtime == oldst.st_mtime
426 and st.st_mtime == oldst.st_mtime
449 and st.st_ctime == oldst.st_ctime
427 and st.st_ctime == oldst.st_ctime
450 and st.st_size == oldst.st_size):
428 and st.st_size == oldst.st_size):
451 return
429 return
452 self.indexstat = st
430 self.indexstat = st
453 flags = v & ~0xFFFF
431 flags = v & ~0xFFFF
454 fmt = v & 0xFFFF
432 fmt = v & 0xFFFF
455 if fmt == REVLOGV0:
433 if fmt == REVLOGV0:
456 if flags:
434 if flags:
457 raise RevlogError(_("index %s unknown flags %#04x for format v0")
435 raise RevlogError(_("index %s unknown flags %#04x for format v0")
458 % (self.indexfile, flags >> 16))
436 % (self.indexfile, flags >> 16))
459 elif fmt == REVLOGNG:
437 elif fmt == REVLOGNG:
460 if flags & ~REVLOGNGINLINEDATA:
438 if flags & ~REVLOGNGINLINEDATA:
461 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
439 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
462 % (self.indexfile, flags >> 16))
440 % (self.indexfile, flags >> 16))
463 else:
441 else:
464 raise RevlogError(_("index %s unknown format %d")
442 raise RevlogError(_("index %s unknown format %d")
465 % (self.indexfile, fmt))
443 % (self.indexfile, fmt))
466 self.version = v
444 self.version = v
467 self.nodemap = {nullid: nullrev}
445 self.nodemap = {nullid: nullrev}
468 self.index = []
446 self.index = []
469 self._io = revlogio()
447 self._io = revlogio()
470 self.indexformat = indexformatng
448 self.indexformat = indexformatng
471 if self.version == REVLOGV0:
449 if self.version == REVLOGV0:
472 self._io = revlogoldio()
450 self._io = revlogoldio()
473 self.indexformat = indexformatv0
451 self.indexformat = indexformatv0
474 if i:
452 if i:
475 self.index, self.nodemap = self._io.parseindex(f, st, self._inline())
453 self.index, self.nodemap = self._io.parseindex(f, st, self._inline())
476
454
477 def _loadindex(self, start, end):
455 def _loadindex(self, start, end):
478 """load a block of indexes all at once from the lazy parser"""
456 """load a block of indexes all at once from the lazy parser"""
479 if isinstance(self.index, lazyindex):
457 if isinstance(self.index, lazyindex):
480 self.index.p.loadindex(start, end)
458 self.index.p.loadindex(start, end)
481
459
482 def _loadindexmap(self):
460 def _loadindexmap(self):
483 """loads both the map and the index from the lazy parser"""
461 """loads both the map and the index from the lazy parser"""
484 if isinstance(self.index, lazyindex):
462 if isinstance(self.index, lazyindex):
485 p = self.index.p
463 p = self.index.p
486 p.loadindex()
464 p.loadindex()
487 self.nodemap = p.map
465 self.nodemap = p.map
488
466
489 def _loadmap(self):
467 def _loadmap(self):
490 """loads the map from the lazy parser"""
468 """loads the map from the lazy parser"""
491 if isinstance(self.nodemap, lazymap):
469 if isinstance(self.nodemap, lazymap):
492 self.nodemap.p.loadmap()
470 self.nodemap.p.loadmap()
493 self.nodemap = self.nodemap.p.map
471 self.nodemap = self.nodemap.p.map
494
472
495 def _inline(self): return self.version & REVLOGNGINLINEDATA
473 def _inline(self): return self.version & REVLOGNGINLINEDATA
496
474
497 def tip(self): return self.node(len(self.index) - 1)
475 def tip(self): return self.node(len(self.index) - 1)
498 def count(self): return len(self.index)
476 def count(self): return len(self.index)
499 def node(self, rev):
477 def node(self, rev):
500 return rev == nullrev and nullid or self.index[rev][-1]
478 return rev == nullrev and nullid or self.index[rev][-1]
501 def rev(self, node):
479 def rev(self, node):
502 try:
480 try:
503 return self.nodemap[node]
481 return self.nodemap[node]
504 except KeyError:
482 except KeyError:
505 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
483 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
506 def linkrev(self, node):
484 def linkrev(self, node):
507 return (node == nullid) and nullrev or self.index[self.rev(node)][-4]
485 return (node == nullid) and nullrev or self.index[self.rev(node)][-4]
508 def parents(self, node):
486 def parents(self, node):
509 if node == nullid: return (nullid, nullid)
487 if node == nullid: return (nullid, nullid)
510 r = self.rev(node)
488 r = self.rev(node)
511 d = self.index[r][-3:-1]
489 d = self.index[r][-3:-1]
512 if self.version == REVLOGV0:
490 if self.version == REVLOGV0:
513 return d
491 return d
514 return (self.node(d[0]), self.node(d[1]))
492 return (self.node(d[0]), self.node(d[1]))
515 def parentrevs(self, rev):
493 def parentrevs(self, rev):
516 if rev == nullrev:
494 if rev == nullrev:
517 return (nullrev, nullrev)
495 return (nullrev, nullrev)
518 d = self.index[rev][-3:-1]
496 d = self.index[rev][-3:-1]
519 if self.version == REVLOGV0:
497 if self.version == REVLOGV0:
520 return (self.rev(d[0]), self.rev(d[1]))
498 return (self.rev(d[0]), self.rev(d[1]))
521 return d
499 return d
522 def start(self, rev):
500 def start(self, rev):
523 if rev == nullrev:
501 if rev == nullrev:
524 return 0
502 return 0
525 if self.version != REVLOGV0:
503 if self.version != REVLOGV0:
526 return getoffset(self.index[rev][0])
504 return getoffset(self.index[rev][0])
527 return self.index[rev][0]
505 return self.index[rev][0]
528
506
529 def end(self, rev): return self.start(rev) + self.length(rev)
507 def end(self, rev): return self.start(rev) + self.length(rev)
530
508
531 def size(self, rev):
509 def size(self, rev):
532 """return the length of the uncompressed text for a given revision"""
510 """return the length of the uncompressed text for a given revision"""
533 if rev == nullrev:
511 if rev == nullrev:
534 return 0
512 return 0
535 l = -1
513 l = -1
536 if self.version != REVLOGV0:
514 if self.version != REVLOGV0:
537 l = self.index[rev][2]
515 l = self.index[rev][2]
538 if l >= 0:
516 if l >= 0:
539 return l
517 return l
540
518
541 t = self.revision(self.node(rev))
519 t = self.revision(self.node(rev))
542 return len(t)
520 return len(t)
543
521
544 # alternate implementation, The advantage to this code is it
522 # alternate implementation, The advantage to this code is it
545 # will be faster for a single revision. But, the results are not
523 # will be faster for a single revision. But, the results are not
546 # cached, so finding the size of every revision will be slower.
524 # cached, so finding the size of every revision will be slower.
547 """
525 """
548 if self.cache and self.cache[1] == rev:
526 if self.cache and self.cache[1] == rev:
549 return len(self.cache[2])
527 return len(self.cache[2])
550
528
551 base = self.base(rev)
529 base = self.base(rev)
552 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
530 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
553 base = self.cache[1]
531 base = self.cache[1]
554 text = self.cache[2]
532 text = self.cache[2]
555 else:
533 else:
556 text = self.revision(self.node(base))
534 text = self.revision(self.node(base))
557
535
558 l = len(text)
536 l = len(text)
559 for x in xrange(base + 1, rev + 1):
537 for x in xrange(base + 1, rev + 1):
560 l = mdiff.patchedsize(l, self.chunk(x))
538 l = mdiff.patchedsize(l, self.chunk(x))
561 return l
539 return l
562 """
540 """
563
541
564 def length(self, rev):
542 def length(self, rev):
565 if rev == nullrev:
543 if rev == nullrev:
566 return 0
544 return 0
567 else:
545 else:
568 return self.index[rev][1]
546 return self.index[rev][1]
569 def base(self, rev):
547 def base(self, rev):
570 if (rev == nullrev):
548 if (rev == nullrev):
571 return nullrev
549 return nullrev
572 else:
550 else:
573 return self.index[rev][-5]
551 return self.index[rev][-5]
574
552
575 def reachable(self, node, stop=None):
553 def reachable(self, node, stop=None):
576 """return a hash of all nodes ancestral to a given node, including
554 """return a hash of all nodes ancestral to a given node, including
577 the node itself, stopping when stop is matched"""
555 the node itself, stopping when stop is matched"""
578 reachable = {}
556 reachable = {}
579 visit = [node]
557 visit = [node]
580 reachable[node] = 1
558 reachable[node] = 1
581 if stop:
559 if stop:
582 stopn = self.rev(stop)
560 stopn = self.rev(stop)
583 else:
561 else:
584 stopn = 0
562 stopn = 0
585 while visit:
563 while visit:
586 n = visit.pop(0)
564 n = visit.pop(0)
587 if n == stop:
565 if n == stop:
588 continue
566 continue
589 if n == nullid:
567 if n == nullid:
590 continue
568 continue
591 for p in self.parents(n):
569 for p in self.parents(n):
592 if self.rev(p) < stopn:
570 if self.rev(p) < stopn:
593 continue
571 continue
594 if p not in reachable:
572 if p not in reachable:
595 reachable[p] = 1
573 reachable[p] = 1
596 visit.append(p)
574 visit.append(p)
597 return reachable
575 return reachable
598
576
599 def nodesbetween(self, roots=None, heads=None):
577 def nodesbetween(self, roots=None, heads=None):
600 """Return a tuple containing three elements. Elements 1 and 2 contain
578 """Return a tuple containing three elements. Elements 1 and 2 contain
601 a final list bases and heads after all the unreachable ones have been
579 a final list bases and heads after all the unreachable ones have been
602 pruned. Element 0 contains a topologically sorted list of all
580 pruned. Element 0 contains a topologically sorted list of all
603
581
604 nodes that satisfy these constraints:
582 nodes that satisfy these constraints:
605 1. All nodes must be descended from a node in roots (the nodes on
583 1. All nodes must be descended from a node in roots (the nodes on
606 roots are considered descended from themselves).
584 roots are considered descended from themselves).
607 2. All nodes must also be ancestors of a node in heads (the nodes in
585 2. All nodes must also be ancestors of a node in heads (the nodes in
608 heads are considered to be their own ancestors).
586 heads are considered to be their own ancestors).
609
587
610 If roots is unspecified, nullid is assumed as the only root.
588 If roots is unspecified, nullid is assumed as the only root.
611 If heads is unspecified, it is taken to be the output of the
589 If heads is unspecified, it is taken to be the output of the
612 heads method (i.e. a list of all nodes in the repository that
590 heads method (i.e. a list of all nodes in the repository that
613 have no children)."""
591 have no children)."""
614 nonodes = ([], [], [])
592 nonodes = ([], [], [])
615 if roots is not None:
593 if roots is not None:
616 roots = list(roots)
594 roots = list(roots)
617 if not roots:
595 if not roots:
618 return nonodes
596 return nonodes
619 lowestrev = min([self.rev(n) for n in roots])
597 lowestrev = min([self.rev(n) for n in roots])
620 else:
598 else:
621 roots = [nullid] # Everybody's a descendent of nullid
599 roots = [nullid] # Everybody's a descendent of nullid
622 lowestrev = nullrev
600 lowestrev = nullrev
623 if (lowestrev == nullrev) and (heads is None):
601 if (lowestrev == nullrev) and (heads is None):
624 # We want _all_ the nodes!
602 # We want _all_ the nodes!
625 return ([self.node(r) for r in xrange(0, self.count())],
603 return ([self.node(r) for r in xrange(0, self.count())],
626 [nullid], list(self.heads()))
604 [nullid], list(self.heads()))
627 if heads is None:
605 if heads is None:
628 # All nodes are ancestors, so the latest ancestor is the last
606 # All nodes are ancestors, so the latest ancestor is the last
629 # node.
607 # node.
630 highestrev = self.count() - 1
608 highestrev = self.count() - 1
631 # Set ancestors to None to signal that every node is an ancestor.
609 # Set ancestors to None to signal that every node is an ancestor.
632 ancestors = None
610 ancestors = None
633 # Set heads to an empty dictionary for later discovery of heads
611 # Set heads to an empty dictionary for later discovery of heads
634 heads = {}
612 heads = {}
635 else:
613 else:
636 heads = list(heads)
614 heads = list(heads)
637 if not heads:
615 if not heads:
638 return nonodes
616 return nonodes
639 ancestors = {}
617 ancestors = {}
640 # Turn heads into a dictionary so we can remove 'fake' heads.
618 # Turn heads into a dictionary so we can remove 'fake' heads.
641 # Also, later we will be using it to filter out the heads we can't
619 # Also, later we will be using it to filter out the heads we can't
642 # find from roots.
620 # find from roots.
643 heads = dict.fromkeys(heads, 0)
621 heads = dict.fromkeys(heads, 0)
644 # Start at the top and keep marking parents until we're done.
622 # Start at the top and keep marking parents until we're done.
645 nodestotag = heads.keys()
623 nodestotag = heads.keys()
646 # Remember where the top was so we can use it as a limit later.
624 # Remember where the top was so we can use it as a limit later.
647 highestrev = max([self.rev(n) for n in nodestotag])
625 highestrev = max([self.rev(n) for n in nodestotag])
648 while nodestotag:
626 while nodestotag:
649 # grab a node to tag
627 # grab a node to tag
650 n = nodestotag.pop()
628 n = nodestotag.pop()
651 # Never tag nullid
629 # Never tag nullid
652 if n == nullid:
630 if n == nullid:
653 continue
631 continue
654 # A node's revision number represents its place in a
632 # A node's revision number represents its place in a
655 # topologically sorted list of nodes.
633 # topologically sorted list of nodes.
656 r = self.rev(n)
634 r = self.rev(n)
657 if r >= lowestrev:
635 if r >= lowestrev:
658 if n not in ancestors:
636 if n not in ancestors:
659 # If we are possibly a descendent of one of the roots
637 # If we are possibly a descendent of one of the roots
660 # and we haven't already been marked as an ancestor
638 # and we haven't already been marked as an ancestor
661 ancestors[n] = 1 # Mark as ancestor
639 ancestors[n] = 1 # Mark as ancestor
662 # Add non-nullid parents to list of nodes to tag.
640 # Add non-nullid parents to list of nodes to tag.
663 nodestotag.extend([p for p in self.parents(n) if
641 nodestotag.extend([p for p in self.parents(n) if
664 p != nullid])
642 p != nullid])
665 elif n in heads: # We've seen it before, is it a fake head?
643 elif n in heads: # We've seen it before, is it a fake head?
666 # So it is, real heads should not be the ancestors of
644 # So it is, real heads should not be the ancestors of
667 # any other heads.
645 # any other heads.
668 heads.pop(n)
646 heads.pop(n)
669 if not ancestors:
647 if not ancestors:
670 return nonodes
648 return nonodes
671 # Now that we have our set of ancestors, we want to remove any
649 # Now that we have our set of ancestors, we want to remove any
672 # roots that are not ancestors.
650 # roots that are not ancestors.
673
651
674 # If one of the roots was nullid, everything is included anyway.
652 # If one of the roots was nullid, everything is included anyway.
675 if lowestrev > nullrev:
653 if lowestrev > nullrev:
676 # But, since we weren't, let's recompute the lowest rev to not
654 # But, since we weren't, let's recompute the lowest rev to not
677 # include roots that aren't ancestors.
655 # include roots that aren't ancestors.
678
656
679 # Filter out roots that aren't ancestors of heads
657 # Filter out roots that aren't ancestors of heads
680 roots = [n for n in roots if n in ancestors]
658 roots = [n for n in roots if n in ancestors]
681 # Recompute the lowest revision
659 # Recompute the lowest revision
682 if roots:
660 if roots:
683 lowestrev = min([self.rev(n) for n in roots])
661 lowestrev = min([self.rev(n) for n in roots])
684 else:
662 else:
685 # No more roots? Return empty list
663 # No more roots? Return empty list
686 return nonodes
664 return nonodes
687 else:
665 else:
688 # We are descending from nullid, and don't need to care about
666 # We are descending from nullid, and don't need to care about
689 # any other roots.
667 # any other roots.
690 lowestrev = nullrev
668 lowestrev = nullrev
691 roots = [nullid]
669 roots = [nullid]
692 # Transform our roots list into a 'set' (i.e. a dictionary where the
670 # Transform our roots list into a 'set' (i.e. a dictionary where the
693 # values don't matter.
671 # values don't matter.
694 descendents = dict.fromkeys(roots, 1)
672 descendents = dict.fromkeys(roots, 1)
695 # Also, keep the original roots so we can filter out roots that aren't
673 # Also, keep the original roots so we can filter out roots that aren't
696 # 'real' roots (i.e. are descended from other roots).
674 # 'real' roots (i.e. are descended from other roots).
697 roots = descendents.copy()
675 roots = descendents.copy()
698 # Our topologically sorted list of output nodes.
676 # Our topologically sorted list of output nodes.
699 orderedout = []
677 orderedout = []
700 # Don't start at nullid since we don't want nullid in our output list,
678 # Don't start at nullid since we don't want nullid in our output list,
701 # and if nullid shows up in descedents, empty parents will look like
679 # and if nullid shows up in descedents, empty parents will look like
702 # they're descendents.
680 # they're descendents.
703 for r in xrange(max(lowestrev, 0), highestrev + 1):
681 for r in xrange(max(lowestrev, 0), highestrev + 1):
704 n = self.node(r)
682 n = self.node(r)
705 isdescendent = False
683 isdescendent = False
706 if lowestrev == nullrev: # Everybody is a descendent of nullid
684 if lowestrev == nullrev: # Everybody is a descendent of nullid
707 isdescendent = True
685 isdescendent = True
708 elif n in descendents:
686 elif n in descendents:
709 # n is already a descendent
687 # n is already a descendent
710 isdescendent = True
688 isdescendent = True
711 # This check only needs to be done here because all the roots
689 # This check only needs to be done here because all the roots
712 # will start being marked is descendents before the loop.
690 # will start being marked is descendents before the loop.
713 if n in roots:
691 if n in roots:
714 # If n was a root, check if it's a 'real' root.
692 # If n was a root, check if it's a 'real' root.
715 p = tuple(self.parents(n))
693 p = tuple(self.parents(n))
716 # If any of its parents are descendents, it's not a root.
694 # If any of its parents are descendents, it's not a root.
717 if (p[0] in descendents) or (p[1] in descendents):
695 if (p[0] in descendents) or (p[1] in descendents):
718 roots.pop(n)
696 roots.pop(n)
719 else:
697 else:
720 p = tuple(self.parents(n))
698 p = tuple(self.parents(n))
721 # A node is a descendent if either of its parents are
699 # A node is a descendent if either of its parents are
722 # descendents. (We seeded the dependents list with the roots
700 # descendents. (We seeded the dependents list with the roots
723 # up there, remember?)
701 # up there, remember?)
724 if (p[0] in descendents) or (p[1] in descendents):
702 if (p[0] in descendents) or (p[1] in descendents):
725 descendents[n] = 1
703 descendents[n] = 1
726 isdescendent = True
704 isdescendent = True
727 if isdescendent and ((ancestors is None) or (n in ancestors)):
705 if isdescendent and ((ancestors is None) or (n in ancestors)):
728 # Only include nodes that are both descendents and ancestors.
706 # Only include nodes that are both descendents and ancestors.
729 orderedout.append(n)
707 orderedout.append(n)
730 if (ancestors is not None) and (n in heads):
708 if (ancestors is not None) and (n in heads):
731 # We're trying to figure out which heads are reachable
709 # We're trying to figure out which heads are reachable
732 # from roots.
710 # from roots.
733 # Mark this head as having been reached
711 # Mark this head as having been reached
734 heads[n] = 1
712 heads[n] = 1
735 elif ancestors is None:
713 elif ancestors is None:
736 # Otherwise, we're trying to discover the heads.
714 # Otherwise, we're trying to discover the heads.
737 # Assume this is a head because if it isn't, the next step
715 # Assume this is a head because if it isn't, the next step
738 # will eventually remove it.
716 # will eventually remove it.
739 heads[n] = 1
717 heads[n] = 1
740 # But, obviously its parents aren't.
718 # But, obviously its parents aren't.
741 for p in self.parents(n):
719 for p in self.parents(n):
742 heads.pop(p, None)
720 heads.pop(p, None)
743 heads = [n for n in heads.iterkeys() if heads[n] != 0]
721 heads = [n for n in heads.iterkeys() if heads[n] != 0]
744 roots = roots.keys()
722 roots = roots.keys()
745 assert orderedout
723 assert orderedout
746 assert roots
724 assert roots
747 assert heads
725 assert heads
748 return (orderedout, roots, heads)
726 return (orderedout, roots, heads)
749
727
750 def heads(self, start=None, stop=None):
728 def heads(self, start=None, stop=None):
751 """return the list of all nodes that have no children
729 """return the list of all nodes that have no children
752
730
753 if start is specified, only heads that are descendants of
731 if start is specified, only heads that are descendants of
754 start will be returned
732 start will be returned
755 if stop is specified, it will consider all the revs from stop
733 if stop is specified, it will consider all the revs from stop
756 as if they had no children
734 as if they had no children
757 """
735 """
758 if start is None:
736 if start is None:
759 start = nullid
737 start = nullid
760 if stop is None:
738 if stop is None:
761 stop = []
739 stop = []
762 stoprevs = dict.fromkeys([self.rev(n) for n in stop])
740 stoprevs = dict.fromkeys([self.rev(n) for n in stop])
763 startrev = self.rev(start)
741 startrev = self.rev(start)
764 reachable = {startrev: 1}
742 reachable = {startrev: 1}
765 heads = {startrev: 1}
743 heads = {startrev: 1}
766
744
767 parentrevs = self.parentrevs
745 parentrevs = self.parentrevs
768 for r in xrange(startrev + 1, self.count()):
746 for r in xrange(startrev + 1, self.count()):
769 for p in parentrevs(r):
747 for p in parentrevs(r):
770 if p in reachable:
748 if p in reachable:
771 if r not in stoprevs:
749 if r not in stoprevs:
772 reachable[r] = 1
750 reachable[r] = 1
773 heads[r] = 1
751 heads[r] = 1
774 if p in heads and p not in stoprevs:
752 if p in heads and p not in stoprevs:
775 del heads[p]
753 del heads[p]
776
754
777 return [self.node(r) for r in heads]
755 return [self.node(r) for r in heads]
778
756
779 def children(self, node):
757 def children(self, node):
780 """find the children of a given node"""
758 """find the children of a given node"""
781 c = []
759 c = []
782 p = self.rev(node)
760 p = self.rev(node)
783 for r in range(p + 1, self.count()):
761 for r in range(p + 1, self.count()):
784 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
762 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
785 if prevs:
763 if prevs:
786 for pr in prevs:
764 for pr in prevs:
787 if pr == p:
765 if pr == p:
788 c.append(self.node(r))
766 c.append(self.node(r))
789 elif p == nullrev:
767 elif p == nullrev:
790 c.append(self.node(r))
768 c.append(self.node(r))
791 return c
769 return c
792
770
793 def _match(self, id):
771 def _match(self, id):
794 if isinstance(id, (long, int)):
772 if isinstance(id, (long, int)):
795 # rev
773 # rev
796 return self.node(id)
774 return self.node(id)
797 if len(id) == 20:
775 if len(id) == 20:
798 # possibly a binary node
776 # possibly a binary node
799 # odds of a binary node being all hex in ASCII are 1 in 10**25
777 # odds of a binary node being all hex in ASCII are 1 in 10**25
800 try:
778 try:
801 node = id
779 node = id
802 r = self.rev(node) # quick search the index
780 r = self.rev(node) # quick search the index
803 return node
781 return node
804 except LookupError:
782 except LookupError:
805 pass # may be partial hex id
783 pass # may be partial hex id
806 try:
784 try:
807 # str(rev)
785 # str(rev)
808 rev = int(id)
786 rev = int(id)
809 if str(rev) != id: raise ValueError
787 if str(rev) != id: raise ValueError
810 if rev < 0: rev = self.count() + rev
788 if rev < 0: rev = self.count() + rev
811 if rev < 0 or rev >= self.count(): raise ValueError
789 if rev < 0 or rev >= self.count(): raise ValueError
812 return self.node(rev)
790 return self.node(rev)
813 except (ValueError, OverflowError):
791 except (ValueError, OverflowError):
814 pass
792 pass
815 if len(id) == 40:
793 if len(id) == 40:
816 try:
794 try:
817 # a full hex nodeid?
795 # a full hex nodeid?
818 node = bin(id)
796 node = bin(id)
819 r = self.rev(node)
797 r = self.rev(node)
820 return node
798 return node
821 except TypeError:
799 except TypeError:
822 pass
800 pass
823
801
824 def _partialmatch(self, id):
802 def _partialmatch(self, id):
825 if len(id) < 40:
803 if len(id) < 40:
826 try:
804 try:
827 # hex(node)[:...]
805 # hex(node)[:...]
828 bin_id = bin(id[:len(id) & ~1]) # grab an even number of digits
806 bin_id = bin(id[:len(id) & ~1]) # grab an even number of digits
829 node = None
807 node = None
830 for n in self.nodemap:
808 for n in self.nodemap:
831 if n.startswith(bin_id) and hex(n).startswith(id):
809 if n.startswith(bin_id) and hex(n).startswith(id):
832 if node is not None:
810 if node is not None:
833 raise LookupError(_("Ambiguous identifier"))
811 raise LookupError(_("Ambiguous identifier"))
834 node = n
812 node = n
835 if node is not None:
813 if node is not None:
836 return node
814 return node
837 except TypeError:
815 except TypeError:
838 pass
816 pass
839
817
840 def lookup(self, id):
818 def lookup(self, id):
841 """locate a node based on:
819 """locate a node based on:
842 - revision number or str(revision number)
820 - revision number or str(revision number)
843 - nodeid or subset of hex nodeid
821 - nodeid or subset of hex nodeid
844 """
822 """
845
823
846 n = self._match(id)
824 n = self._match(id)
847 if n is not None:
825 if n is not None:
848 return n
826 return n
849 n = self._partialmatch(id)
827 n = self._partialmatch(id)
850 if n:
828 if n:
851 return n
829 return n
852
830
853 raise LookupError(_("No match found"))
831 raise LookupError(_("No match found"))
854
832
855 def cmp(self, node, text):
833 def cmp(self, node, text):
856 """compare text with a given file revision"""
834 """compare text with a given file revision"""
857 p1, p2 = self.parents(node)
835 p1, p2 = self.parents(node)
858 return hash(text, p1, p2) != node
836 return hash(text, p1, p2) != node
859
837
860 def diff(self, a, b):
838 def diff(self, a, b):
861 """return a delta between two revisions"""
839 """return a delta between two revisions"""
862 return mdiff.textdiff(a, b)
840 return mdiff.textdiff(a, b)
863
841
864 def patches(self, t, pl):
842 def patches(self, t, pl):
865 """apply a list of patches to a string"""
843 """apply a list of patches to a string"""
866 return mdiff.patches(t, pl)
844 return mdiff.patches(t, pl)
867
845
868 def chunk(self, rev, df=None, cachelen=4096):
846 def chunk(self, rev, df=None, cachelen=4096):
869 start, length = self.start(rev), self.length(rev)
847 start, length = self.start(rev), self.length(rev)
870 inline = self._inline()
848 inline = self._inline()
871 if inline:
849 if inline:
872 start += (rev + 1) * struct.calcsize(self.indexformat)
850 start += (rev + 1) * struct.calcsize(self.indexformat)
873 end = start + length
851 end = start + length
874 def loadcache(df):
852 def loadcache(df):
875 cache_length = max(cachelen, length) # 4k
853 cache_length = max(cachelen, length) # 4k
876 if not df:
854 if not df:
877 if inline:
855 if inline:
878 df = self.opener(self.indexfile)
856 df = self.opener(self.indexfile)
879 else:
857 else:
880 df = self.opener(self.datafile)
858 df = self.opener(self.datafile)
881 df.seek(start)
859 df.seek(start)
882 self._io.chunkcache = (start, df.read(cache_length))
860 self._io.chunkcache = (start, df.read(cache_length))
883
861
884 if not self._io.chunkcache:
862 if not self._io.chunkcache:
885 loadcache(df)
863 loadcache(df)
886
864
887 cache_start = self._io.chunkcache[0]
865 cache_start = self._io.chunkcache[0]
888 cache_end = cache_start + len(self._io.chunkcache[1])
866 cache_end = cache_start + len(self._io.chunkcache[1])
889 if start >= cache_start and end <= cache_end:
867 if start >= cache_start and end <= cache_end:
890 # it is cached
868 # it is cached
891 offset = start - cache_start
869 offset = start - cache_start
892 else:
870 else:
893 loadcache(df)
871 loadcache(df)
894 offset = 0
872 offset = 0
895
873
896 #def checkchunk():
874 #def checkchunk():
897 # df = self.opener(self.datafile)
875 # df = self.opener(self.datafile)
898 # df.seek(start)
876 # df.seek(start)
899 # return df.read(length)
877 # return df.read(length)
900 #assert s == checkchunk()
878 #assert s == checkchunk()
901 return decompress(self._io.chunkcache[1][offset:offset + length])
879 return decompress(self._io.chunkcache[1][offset:offset + length])
902
880
903 def delta(self, node):
881 def delta(self, node):
904 """return or calculate a delta between a node and its predecessor"""
882 """return or calculate a delta between a node and its predecessor"""
905 r = self.rev(node)
883 r = self.rev(node)
906 return self.revdiff(r - 1, r)
884 return self.revdiff(r - 1, r)
907
885
908 def revdiff(self, rev1, rev2):
886 def revdiff(self, rev1, rev2):
909 """return or calculate a delta between two revisions"""
887 """return or calculate a delta between two revisions"""
910 b1 = self.base(rev1)
888 b1 = self.base(rev1)
911 b2 = self.base(rev2)
889 b2 = self.base(rev2)
912 if b1 == b2 and rev1 + 1 == rev2:
890 if b1 == b2 and rev1 + 1 == rev2:
913 return self.chunk(rev2)
891 return self.chunk(rev2)
914 else:
892 else:
915 return self.diff(self.revision(self.node(rev1)),
893 return self.diff(self.revision(self.node(rev1)),
916 self.revision(self.node(rev2)))
894 self.revision(self.node(rev2)))
917
895
918 def revision(self, node):
896 def revision(self, node):
919 """return an uncompressed revision of a given"""
897 """return an uncompressed revision of a given"""
920 if node == nullid: return ""
898 if node == nullid: return ""
921 if self.cache and self.cache[0] == node: return self.cache[2]
899 if self.cache and self.cache[0] == node: return self.cache[2]
922
900
923 # look up what we need to read
901 # look up what we need to read
924 text = None
902 text = None
925 rev = self.rev(node)
903 rev = self.rev(node)
926 base = self.base(rev)
904 base = self.base(rev)
927
905
928 if self._inline():
906 if self._inline():
929 # we probably have the whole chunk cached
907 # we probably have the whole chunk cached
930 df = None
908 df = None
931 else:
909 else:
932 df = self.opener(self.datafile)
910 df = self.opener(self.datafile)
933
911
934 # do we have useful data cached?
912 # do we have useful data cached?
935 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
913 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
936 base = self.cache[1]
914 base = self.cache[1]
937 text = self.cache[2]
915 text = self.cache[2]
938 self._loadindex(base, rev + 1)
916 self._loadindex(base, rev + 1)
939 else:
917 else:
940 self._loadindex(base, rev + 1)
918 self._loadindex(base, rev + 1)
941 text = self.chunk(base, df=df)
919 text = self.chunk(base, df=df)
942
920
943 bins = []
921 bins = []
944 for r in xrange(base + 1, rev + 1):
922 for r in xrange(base + 1, rev + 1):
945 bins.append(self.chunk(r, df=df))
923 bins.append(self.chunk(r, df=df))
946
924
947 text = self.patches(text, bins)
925 text = self.patches(text, bins)
948
926
949 p1, p2 = self.parents(node)
927 p1, p2 = self.parents(node)
950 if node != hash(text, p1, p2):
928 if node != hash(text, p1, p2):
951 raise RevlogError(_("integrity check failed on %s:%d")
929 raise RevlogError(_("integrity check failed on %s:%d")
952 % (self.datafile, rev))
930 % (self.datafile, rev))
953
931
954 self.cache = (node, rev, text)
932 self.cache = (node, rev, text)
955 return text
933 return text
956
934
957 def checkinlinesize(self, tr, fp=None):
935 def checkinlinesize(self, tr, fp=None):
958 if not self._inline():
936 if not self._inline():
959 return
937 return
960 if not fp:
938 if not fp:
961 fp = self.opener(self.indexfile, 'r')
939 fp = self.opener(self.indexfile, 'r')
962 fp.seek(0, 2)
940 fp.seek(0, 2)
963 size = fp.tell()
941 size = fp.tell()
964 if size < 131072:
942 if size < 131072:
965 return
943 return
966 trinfo = tr.find(self.indexfile)
944 trinfo = tr.find(self.indexfile)
967 if trinfo == None:
945 if trinfo == None:
968 raise RevlogError(_("%s not found in the transaction")
946 raise RevlogError(_("%s not found in the transaction")
969 % self.indexfile)
947 % self.indexfile)
970
948
971 trindex = trinfo[2]
949 trindex = trinfo[2]
972 dataoff = self.start(trindex)
950 dataoff = self.start(trindex)
973
951
974 tr.add(self.datafile, dataoff)
952 tr.add(self.datafile, dataoff)
975 df = self.opener(self.datafile, 'w')
953 df = self.opener(self.datafile, 'w')
976 calc = struct.calcsize(self.indexformat)
954 calc = struct.calcsize(self.indexformat)
977 for r in xrange(self.count()):
955 for r in xrange(self.count()):
978 start = self.start(r) + (r + 1) * calc
956 start = self.start(r) + (r + 1) * calc
979 length = self.length(r)
957 length = self.length(r)
980 fp.seek(start)
958 fp.seek(start)
981 d = fp.read(length)
959 d = fp.read(length)
982 df.write(d)
960 df.write(d)
983 fp.close()
961 fp.close()
984 df.close()
962 df.close()
985 fp = self.opener(self.indexfile, 'w', atomictemp=True)
963 fp = self.opener(self.indexfile, 'w', atomictemp=True)
986 self.version &= ~(REVLOGNGINLINEDATA)
964 self.version &= ~(REVLOGNGINLINEDATA)
987 if self.count():
965 if self.count():
988 x = self.index[0]
966 x = self.index[0]
989 e = struct.pack(self.indexformat, *x)[4:]
967 e = struct.pack(self.indexformat, *x)[4:]
990 l = struct.pack(versionformat, self.version)
968 l = struct.pack(versionformat, self.version)
991 fp.write(l)
969 fp.write(l)
992 fp.write(e)
970 fp.write(e)
993
971
994 for i in xrange(1, self.count()):
972 for i in xrange(1, self.count()):
995 x = self.index[i]
973 x = self.index[i]
996 e = struct.pack(self.indexformat, *x)
974 e = struct.pack(self.indexformat, *x)
997 fp.write(e)
975 fp.write(e)
998
976
999 # if we don't call rename, the temp file will never replace the
977 # if we don't call rename, the temp file will never replace the
1000 # real index
978 # real index
1001 fp.rename()
979 fp.rename()
1002
980
1003 tr.replace(self.indexfile, trindex * calc)
981 tr.replace(self.indexfile, trindex * calc)
1004 self._io.chunkcache = None
982 self._io.chunkcache = None
1005
983
1006 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
984 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
1007 """add a revision to the log
985 """add a revision to the log
1008
986
1009 text - the revision data to add
987 text - the revision data to add
1010 transaction - the transaction object used for rollback
988 transaction - the transaction object used for rollback
1011 link - the linkrev data to add
989 link - the linkrev data to add
1012 p1, p2 - the parent nodeids of the revision
990 p1, p2 - the parent nodeids of the revision
1013 d - an optional precomputed delta
991 d - an optional precomputed delta
1014 """
992 """
1015 if not self._inline():
993 if not self._inline():
1016 dfh = self.opener(self.datafile, "a")
994 dfh = self.opener(self.datafile, "a")
1017 else:
995 else:
1018 dfh = None
996 dfh = None
1019 ifh = self.opener(self.indexfile, "a+")
997 ifh = self.opener(self.indexfile, "a+")
1020 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
998 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
1021
999
1022 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
1000 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
1023 if text is None: text = ""
1001 if text is None: text = ""
1024 if p1 is None: p1 = self.tip()
1002 if p1 is None: p1 = self.tip()
1025 if p2 is None: p2 = nullid
1003 if p2 is None: p2 = nullid
1026
1004
1027 node = hash(text, p1, p2)
1005 node = hash(text, p1, p2)
1028
1006
1029 if node in self.nodemap:
1007 if node in self.nodemap:
1030 return node
1008 return node
1031
1009
1032 n = self.count()
1010 n = self.count()
1033 t = n - 1
1011 t = n - 1
1034
1012
1035 if n:
1013 if n:
1036 base = self.base(t)
1014 base = self.base(t)
1037 start = self.start(base)
1015 start = self.start(base)
1038 end = self.end(t)
1016 end = self.end(t)
1039 if not d:
1017 if not d:
1040 prev = self.revision(self.tip())
1018 prev = self.revision(self.tip())
1041 d = self.diff(prev, text)
1019 d = self.diff(prev, text)
1042 data = compress(d)
1020 data = compress(d)
1043 l = len(data[1]) + len(data[0])
1021 l = len(data[1]) + len(data[0])
1044 dist = end - start + l
1022 dist = end - start + l
1045
1023
1046 # full versions are inserted when the needed deltas
1024 # full versions are inserted when the needed deltas
1047 # become comparable to the uncompressed text
1025 # become comparable to the uncompressed text
1048 if not n or dist > len(text) * 2:
1026 if not n or dist > len(text) * 2:
1049 data = compress(text)
1027 data = compress(text)
1050 l = len(data[1]) + len(data[0])
1028 l = len(data[1]) + len(data[0])
1051 base = n
1029 base = n
1052 else:
1030 else:
1053 base = self.base(t)
1031 base = self.base(t)
1054
1032
1055 offset = 0
1033 offset = 0
1056 if t >= 0:
1034 if t >= 0:
1057 offset = self.end(t)
1035 offset = self.end(t)
1058
1036
1059 if self.version == REVLOGV0:
1037 if self.version == REVLOGV0:
1060 e = (offset, l, base, link, p1, p2, node)
1038 e = (offset, l, base, link, p1, p2, node)
1061 else:
1039 else:
1062 e = (offset_type(offset, 0), l, len(text),
1040 e = (offset_type(offset, 0), l, len(text),
1063 base, link, self.rev(p1), self.rev(p2), node)
1041 base, link, self.rev(p1), self.rev(p2), node)
1064
1042
1065 self.index.append(e)
1043 self.index.append(e)
1066 self.nodemap[node] = n
1044 self.nodemap[node] = n
1067 entry = struct.pack(self.indexformat, *e)
1045 entry = struct.pack(self.indexformat, *e)
1068
1046
1069 if not self._inline():
1047 if not self._inline():
1070 transaction.add(self.datafile, offset)
1048 transaction.add(self.datafile, offset)
1071 transaction.add(self.indexfile, n * len(entry))
1049 transaction.add(self.indexfile, n * len(entry))
1072 if data[0]:
1050 if data[0]:
1073 dfh.write(data[0])
1051 dfh.write(data[0])
1074 dfh.write(data[1])
1052 dfh.write(data[1])
1075 dfh.flush()
1053 dfh.flush()
1076 else:
1054 else:
1077 ifh.seek(0, 2)
1055 ifh.seek(0, 2)
1078 transaction.add(self.indexfile, ifh.tell(), self.count() - 1)
1056 transaction.add(self.indexfile, ifh.tell(), self.count() - 1)
1079
1057
1080 if len(self.index) == 1 and self.version != REVLOGV0:
1058 if len(self.index) == 1 and self.version != REVLOGV0:
1081 l = struct.pack(versionformat, self.version)
1059 l = struct.pack(versionformat, self.version)
1082 ifh.write(l)
1060 ifh.write(l)
1083 entry = entry[4:]
1061 entry = entry[4:]
1084
1062
1085 ifh.write(entry)
1063 ifh.write(entry)
1086
1064
1087 if self._inline():
1065 if self._inline():
1088 ifh.write(data[0])
1066 ifh.write(data[0])
1089 ifh.write(data[1])
1067 ifh.write(data[1])
1090 self.checkinlinesize(transaction, ifh)
1068 self.checkinlinesize(transaction, ifh)
1091
1069
1092 self.cache = (node, n, text)
1070 self.cache = (node, n, text)
1093 return node
1071 return node
1094
1072
1095 def ancestor(self, a, b):
1073 def ancestor(self, a, b):
1096 """calculate the least common ancestor of nodes a and b"""
1074 """calculate the least common ancestor of nodes a and b"""
1097
1075
1098 def parents(rev):
1076 def parents(rev):
1099 return [p for p in self.parentrevs(rev) if p != nullrev]
1077 return [p for p in self.parentrevs(rev) if p != nullrev]
1100
1078
1101 c = ancestor.ancestor(self.rev(a), self.rev(b), parents)
1079 c = ancestor.ancestor(self.rev(a), self.rev(b), parents)
1102 if c is None:
1080 if c is None:
1103 return nullid
1081 return nullid
1104
1082
1105 return self.node(c)
1083 return self.node(c)
1106
1084
1107 def group(self, nodelist, lookup, infocollect=None):
1085 def group(self, nodelist, lookup, infocollect=None):
1108 """calculate a delta group
1086 """calculate a delta group
1109
1087
1110 Given a list of changeset revs, return a set of deltas and
1088 Given a list of changeset revs, return a set of deltas and
1111 metadata corresponding to nodes. the first delta is
1089 metadata corresponding to nodes. the first delta is
1112 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
1090 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
1113 have this parent as it has all history before these
1091 have this parent as it has all history before these
1114 changesets. parent is parent[0]
1092 changesets. parent is parent[0]
1115 """
1093 """
1116 revs = [self.rev(n) for n in nodelist]
1094 revs = [self.rev(n) for n in nodelist]
1117
1095
1118 # if we don't have any revisions touched by these changesets, bail
1096 # if we don't have any revisions touched by these changesets, bail
1119 if not revs:
1097 if not revs:
1120 yield changegroup.closechunk()
1098 yield changegroup.closechunk()
1121 return
1099 return
1122
1100
1123 # add the parent of the first rev
1101 # add the parent of the first rev
1124 p = self.parents(self.node(revs[0]))[0]
1102 p = self.parents(self.node(revs[0]))[0]
1125 revs.insert(0, self.rev(p))
1103 revs.insert(0, self.rev(p))
1126
1104
1127 # build deltas
1105 # build deltas
1128 for d in xrange(0, len(revs) - 1):
1106 for d in xrange(0, len(revs) - 1):
1129 a, b = revs[d], revs[d + 1]
1107 a, b = revs[d], revs[d + 1]
1130 nb = self.node(b)
1108 nb = self.node(b)
1131
1109
1132 if infocollect is not None:
1110 if infocollect is not None:
1133 infocollect(nb)
1111 infocollect(nb)
1134
1112
1135 d = self.revdiff(a, b)
1113 d = self.revdiff(a, b)
1136 p = self.parents(nb)
1114 p = self.parents(nb)
1137 meta = nb + p[0] + p[1] + lookup(nb)
1115 meta = nb + p[0] + p[1] + lookup(nb)
1138 yield changegroup.genchunk("%s%s" % (meta, d))
1116 yield changegroup.genchunk("%s%s" % (meta, d))
1139
1117
1140 yield changegroup.closechunk()
1118 yield changegroup.closechunk()
1141
1119
1142 def addgroup(self, revs, linkmapper, transaction, unique=0):
1120 def addgroup(self, revs, linkmapper, transaction, unique=0):
1143 """
1121 """
1144 add a delta group
1122 add a delta group
1145
1123
1146 given a set of deltas, add them to the revision log. the
1124 given a set of deltas, add them to the revision log. the
1147 first delta is against its parent, which should be in our
1125 first delta is against its parent, which should be in our
1148 log, the rest are against the previous delta.
1126 log, the rest are against the previous delta.
1149 """
1127 """
1150
1128
1151 #track the base of the current delta log
1129 #track the base of the current delta log
1152 r = self.count()
1130 r = self.count()
1153 t = r - 1
1131 t = r - 1
1154 node = None
1132 node = None
1155
1133
1156 base = prev = nullrev
1134 base = prev = nullrev
1157 start = end = textlen = 0
1135 start = end = textlen = 0
1158 if r:
1136 if r:
1159 end = self.end(t)
1137 end = self.end(t)
1160
1138
1161 ifh = self.opener(self.indexfile, "a+")
1139 ifh = self.opener(self.indexfile, "a+")
1162 ifh.seek(0, 2)
1140 ifh.seek(0, 2)
1163 transaction.add(self.indexfile, ifh.tell(), self.count())
1141 transaction.add(self.indexfile, ifh.tell(), self.count())
1164 if self._inline():
1142 if self._inline():
1165 dfh = None
1143 dfh = None
1166 else:
1144 else:
1167 transaction.add(self.datafile, end)
1145 transaction.add(self.datafile, end)
1168 dfh = self.opener(self.datafile, "a")
1146 dfh = self.opener(self.datafile, "a")
1169
1147
1170 # loop through our set of deltas
1148 # loop through our set of deltas
1171 chain = None
1149 chain = None
1172 for chunk in revs:
1150 for chunk in revs:
1173 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
1151 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
1174 link = linkmapper(cs)
1152 link = linkmapper(cs)
1175 if node in self.nodemap:
1153 if node in self.nodemap:
1176 # this can happen if two branches make the same change
1154 # this can happen if two branches make the same change
1177 # if unique:
1155 # if unique:
1178 # raise RevlogError(_("already have %s") % hex(node[:4]))
1156 # raise RevlogError(_("already have %s") % hex(node[:4]))
1179 chain = node
1157 chain = node
1180 continue
1158 continue
1181 delta = chunk[80:]
1159 delta = chunk[80:]
1182
1160
1183 for p in (p1, p2):
1161 for p in (p1, p2):
1184 if not p in self.nodemap:
1162 if not p in self.nodemap:
1185 raise LookupError(_("unknown parent %s") % short(p))
1163 raise LookupError(_("unknown parent %s") % short(p))
1186
1164
1187 if not chain:
1165 if not chain:
1188 # retrieve the parent revision of the delta chain
1166 # retrieve the parent revision of the delta chain
1189 chain = p1
1167 chain = p1
1190 if not chain in self.nodemap:
1168 if not chain in self.nodemap:
1191 raise LookupError(_("unknown base %s") % short(chain[:4]))
1169 raise LookupError(_("unknown base %s") % short(chain[:4]))
1192
1170
1193 # full versions are inserted when the needed deltas become
1171 # full versions are inserted when the needed deltas become
1194 # comparable to the uncompressed text or when the previous
1172 # comparable to the uncompressed text or when the previous
1195 # version is not the one we have a delta against. We use
1173 # version is not the one we have a delta against. We use
1196 # the size of the previous full rev as a proxy for the
1174 # the size of the previous full rev as a proxy for the
1197 # current size.
1175 # current size.
1198
1176
1199 if chain == prev:
1177 if chain == prev:
1200 tempd = compress(delta)
1178 tempd = compress(delta)
1201 cdelta = tempd[0] + tempd[1]
1179 cdelta = tempd[0] + tempd[1]
1202 textlen = mdiff.patchedsize(textlen, delta)
1180 textlen = mdiff.patchedsize(textlen, delta)
1203
1181
1204 if chain != prev or (end - start + len(cdelta)) > textlen * 2:
1182 if chain != prev or (end - start + len(cdelta)) > textlen * 2:
1205 # flush our writes here so we can read it in revision
1183 # flush our writes here so we can read it in revision
1206 if dfh:
1184 if dfh:
1207 dfh.flush()
1185 dfh.flush()
1208 ifh.flush()
1186 ifh.flush()
1209 text = self.revision(chain)
1187 text = self.revision(chain)
1210 text = self.patches(text, [delta])
1188 text = self.patches(text, [delta])
1211 chk = self._addrevision(text, transaction, link, p1, p2, None,
1189 chk = self._addrevision(text, transaction, link, p1, p2, None,
1212 ifh, dfh)
1190 ifh, dfh)
1213 if not dfh and not self._inline():
1191 if not dfh and not self._inline():
1214 # addrevision switched from inline to conventional
1192 # addrevision switched from inline to conventional
1215 # reopen the index
1193 # reopen the index
1216 dfh = self.opener(self.datafile, "a")
1194 dfh = self.opener(self.datafile, "a")
1217 ifh = self.opener(self.indexfile, "a")
1195 ifh = self.opener(self.indexfile, "a")
1218 if chk != node:
1196 if chk != node:
1219 raise RevlogError(_("consistency error adding group"))
1197 raise RevlogError(_("consistency error adding group"))
1220 textlen = len(text)
1198 textlen = len(text)
1221 else:
1199 else:
1222 if self.version == REVLOGV0:
1200 if self.version == REVLOGV0:
1223 e = (end, len(cdelta), base, link, p1, p2, node)
1201 e = (end, len(cdelta), base, link, p1, p2, node)
1224 else:
1202 else:
1225 e = (offset_type(end, 0), len(cdelta), textlen, base,
1203 e = (offset_type(end, 0), len(cdelta), textlen, base,
1226 link, self.rev(p1), self.rev(p2), node)
1204 link, self.rev(p1), self.rev(p2), node)
1227 self.index.append(e)
1205 self.index.append(e)
1228 self.nodemap[node] = r
1206 self.nodemap[node] = r
1229 if self._inline():
1207 if self._inline():
1230 ifh.write(struct.pack(self.indexformat, *e))
1208 ifh.write(struct.pack(self.indexformat, *e))
1231 ifh.write(cdelta)
1209 ifh.write(cdelta)
1232 self.checkinlinesize(transaction, ifh)
1210 self.checkinlinesize(transaction, ifh)
1233 if not self._inline():
1211 if not self._inline():
1234 dfh = self.opener(self.datafile, "a")
1212 dfh = self.opener(self.datafile, "a")
1235 ifh = self.opener(self.indexfile, "a")
1213 ifh = self.opener(self.indexfile, "a")
1236 else:
1214 else:
1237 dfh.write(cdelta)
1215 dfh.write(cdelta)
1238 ifh.write(struct.pack(self.indexformat, *e))
1216 ifh.write(struct.pack(self.indexformat, *e))
1239
1217
1240 t, r, chain, prev = r, r + 1, node, node
1218 t, r, chain, prev = r, r + 1, node, node
1241 base = self.base(t)
1219 base = self.base(t)
1242 start = self.start(base)
1220 start = self.start(base)
1243 end = self.end(t)
1221 end = self.end(t)
1244
1222
1245 return node
1223 return node
1246
1224
1247 def strip(self, rev, minlink):
1225 def strip(self, rev, minlink):
1248 if self.count() == 0 or rev >= self.count():
1226 if self.count() == 0 or rev >= self.count():
1249 return
1227 return
1250
1228
1251 if isinstance(self.index, lazyindex):
1229 if isinstance(self.index, lazyindex):
1252 self._loadindexmap()
1230 self._loadindexmap()
1253
1231
1254 # When stripping away a revision, we need to make sure it
1232 # When stripping away a revision, we need to make sure it
1255 # does not actually belong to an older changeset.
1233 # does not actually belong to an older changeset.
1256 # The minlink parameter defines the oldest revision
1234 # The minlink parameter defines the oldest revision
1257 # we're allowed to strip away.
1235 # we're allowed to strip away.
1258 while minlink > self.index[rev][-4]:
1236 while minlink > self.index[rev][-4]:
1259 rev += 1
1237 rev += 1
1260 if rev >= self.count():
1238 if rev >= self.count():
1261 return
1239 return
1262
1240
1263 # first truncate the files on disk
1241 # first truncate the files on disk
1264 end = self.start(rev)
1242 end = self.start(rev)
1265 if not self._inline():
1243 if not self._inline():
1266 df = self.opener(self.datafile, "a")
1244 df = self.opener(self.datafile, "a")
1267 df.truncate(end)
1245 df.truncate(end)
1268 end = rev * struct.calcsize(self.indexformat)
1246 end = rev * struct.calcsize(self.indexformat)
1269 else:
1247 else:
1270 end += rev * struct.calcsize(self.indexformat)
1248 end += rev * struct.calcsize(self.indexformat)
1271
1249
1272 indexf = self.opener(self.indexfile, "a")
1250 indexf = self.opener(self.indexfile, "a")
1273 indexf.truncate(end)
1251 indexf.truncate(end)
1274
1252
1275 # then reset internal state in memory to forget those revisions
1253 # then reset internal state in memory to forget those revisions
1276 self.cache = None
1254 self.cache = None
1277 self._io.chunkcache = None
1255 self._io.chunkcache = None
1278 for x in xrange(rev, self.count()):
1256 for x in xrange(rev, self.count()):
1279 del self.nodemap[self.node(x)]
1257 del self.nodemap[self.node(x)]
1280
1258
1281 del self.index[rev:]
1259 del self.index[rev:]
1282
1260
1283 def checksize(self):
1261 def checksize(self):
1284 expected = 0
1262 expected = 0
1285 if self.count():
1263 if self.count():
1286 expected = self.end(self.count() - 1)
1264 expected = self.end(self.count() - 1)
1287
1265
1288 try:
1266 try:
1289 f = self.opener(self.datafile)
1267 f = self.opener(self.datafile)
1290 f.seek(0, 2)
1268 f.seek(0, 2)
1291 actual = f.tell()
1269 actual = f.tell()
1292 dd = actual - expected
1270 dd = actual - expected
1293 except IOError, inst:
1271 except IOError, inst:
1294 if inst.errno != errno.ENOENT:
1272 if inst.errno != errno.ENOENT:
1295 raise
1273 raise
1296 dd = 0
1274 dd = 0
1297
1275
1298 try:
1276 try:
1299 f = self.opener(self.indexfile)
1277 f = self.opener(self.indexfile)
1300 f.seek(0, 2)
1278 f.seek(0, 2)
1301 actual = f.tell()
1279 actual = f.tell()
1302 s = struct.calcsize(self.indexformat)
1280 s = struct.calcsize(self.indexformat)
1303 i = actual / s
1281 i = actual / s
1304 di = actual - (i * s)
1282 di = actual - (i * s)
1305 if self._inline():
1283 if self._inline():
1306 databytes = 0
1284 databytes = 0
1307 for r in xrange(self.count()):
1285 for r in xrange(self.count()):
1308 databytes += self.length(r)
1286 databytes += self.length(r)
1309 dd = 0
1287 dd = 0
1310 di = actual - self.count() * s - databytes
1288 di = actual - self.count() * s - databytes
1311 except IOError, inst:
1289 except IOError, inst:
1312 if inst.errno != errno.ENOENT:
1290 if inst.errno != errno.ENOENT:
1313 raise
1291 raise
1314 di = 0
1292 di = 0
1315
1293
1316 return (dd, di)
1294 return (dd, di)
1317
1295
1318
1296
General Comments 0
You need to be logged in to leave comments. Login now