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