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