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