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