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