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