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