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