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