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