##// END OF EJS Templates
revlog: allow reverse iteration with revlog.revs...
Pierre-Yves David -
r17975:c56b5b65 default
parent child Browse files
Show More
@@ -1,1353 +1,1356 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 return iter(xrange(len(self)))
257 return iter(xrange(len(self)))
258 def revs(self, start=0, stop=None):
258 def revs(self, start=0, stop=None):
259 """iterate over all rev in this revlog (from start to stop)"""
259 """iterate over all rev in this revlog (from start to stop)"""
260 if stop is None:
260 step = 1
261 stop = len(self)
261 if stop is not None:
262 if start > stop:
263 step = -1
264 stop += step
262 else:
265 else:
263 stop += 1
266 stop = len(self)
264 return xrange(start, stop)
267 return xrange(start, stop, step)
265
268
266 @util.propertycache
269 @util.propertycache
267 def nodemap(self):
270 def nodemap(self):
268 self.rev(self.node(0))
271 self.rev(self.node(0))
269 return self._nodecache
272 return self._nodecache
270
273
271 def hasnode(self, node):
274 def hasnode(self, node):
272 try:
275 try:
273 self.rev(node)
276 self.rev(node)
274 return True
277 return True
275 except KeyError:
278 except KeyError:
276 return False
279 return False
277
280
278 def clearcaches(self):
281 def clearcaches(self):
279 try:
282 try:
280 self._nodecache.clearcaches()
283 self._nodecache.clearcaches()
281 except AttributeError:
284 except AttributeError:
282 self._nodecache = {nullid: nullrev}
285 self._nodecache = {nullid: nullrev}
283 self._nodepos = None
286 self._nodepos = None
284
287
285 def rev(self, node):
288 def rev(self, node):
286 try:
289 try:
287 return self._nodecache[node]
290 return self._nodecache[node]
288 except RevlogError:
291 except RevlogError:
289 # parsers.c radix tree lookup failed
292 # parsers.c radix tree lookup failed
290 raise LookupError(node, self.indexfile, _('no node'))
293 raise LookupError(node, self.indexfile, _('no node'))
291 except KeyError:
294 except KeyError:
292 # pure python cache lookup failed
295 # pure python cache lookup failed
293 n = self._nodecache
296 n = self._nodecache
294 i = self.index
297 i = self.index
295 p = self._nodepos
298 p = self._nodepos
296 if p is None:
299 if p is None:
297 p = len(i) - 2
300 p = len(i) - 2
298 for r in xrange(p, -1, -1):
301 for r in xrange(p, -1, -1):
299 v = i[r][7]
302 v = i[r][7]
300 n[v] = r
303 n[v] = r
301 if v == node:
304 if v == node:
302 self._nodepos = r - 1
305 self._nodepos = r - 1
303 return r
306 return r
304 raise LookupError(node, self.indexfile, _('no node'))
307 raise LookupError(node, self.indexfile, _('no node'))
305
308
306 def node(self, rev):
309 def node(self, rev):
307 return self.index[rev][7]
310 return self.index[rev][7]
308 def linkrev(self, rev):
311 def linkrev(self, rev):
309 return self.index[rev][4]
312 return self.index[rev][4]
310 def parents(self, node):
313 def parents(self, node):
311 i = self.index
314 i = self.index
312 d = i[self.rev(node)]
315 d = i[self.rev(node)]
313 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
316 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
314 def parentrevs(self, rev):
317 def parentrevs(self, rev):
315 return self.index[rev][5:7]
318 return self.index[rev][5:7]
316 def start(self, rev):
319 def start(self, rev):
317 return int(self.index[rev][0] >> 16)
320 return int(self.index[rev][0] >> 16)
318 def end(self, rev):
321 def end(self, rev):
319 return self.start(rev) + self.length(rev)
322 return self.start(rev) + self.length(rev)
320 def length(self, rev):
323 def length(self, rev):
321 return self.index[rev][1]
324 return self.index[rev][1]
322 def chainbase(self, rev):
325 def chainbase(self, rev):
323 index = self.index
326 index = self.index
324 base = index[rev][3]
327 base = index[rev][3]
325 while base != rev:
328 while base != rev:
326 rev = base
329 rev = base
327 base = index[rev][3]
330 base = index[rev][3]
328 return base
331 return base
329 def flags(self, rev):
332 def flags(self, rev):
330 return self.index[rev][0] & 0xFFFF
333 return self.index[rev][0] & 0xFFFF
331 def rawsize(self, rev):
334 def rawsize(self, rev):
332 """return the length of the uncompressed text for a given revision"""
335 """return the length of the uncompressed text for a given revision"""
333 l = self.index[rev][2]
336 l = self.index[rev][2]
334 if l >= 0:
337 if l >= 0:
335 return l
338 return l
336
339
337 t = self.revision(self.node(rev))
340 t = self.revision(self.node(rev))
338 return len(t)
341 return len(t)
339 size = rawsize
342 size = rawsize
340
343
341 def ancestors(self, revs, stoprev=0):
344 def ancestors(self, revs, stoprev=0):
342 """Generate the ancestors of 'revs' in reverse topological order.
345 """Generate the ancestors of 'revs' in reverse topological order.
343 Does not generate revs lower than stoprev.
346 Does not generate revs lower than stoprev.
344
347
345 Yield a sequence of revision numbers starting with the parents
348 Yield a sequence of revision numbers starting with the parents
346 of each revision in revs, i.e., each revision is *not* considered
349 of each revision in revs, i.e., each revision is *not* considered
347 an ancestor of itself. Results are in breadth-first order:
350 an ancestor of itself. Results are in breadth-first order:
348 parents of each rev in revs, then parents of those, etc. Result
351 parents of each rev in revs, then parents of those, etc. Result
349 does not include the null revision."""
352 does not include the null revision."""
350 visit = util.deque(revs)
353 visit = util.deque(revs)
351 seen = set([nullrev])
354 seen = set([nullrev])
352 while visit:
355 while visit:
353 for parent in self.parentrevs(visit.popleft()):
356 for parent in self.parentrevs(visit.popleft()):
354 if parent < stoprev:
357 if parent < stoprev:
355 continue
358 continue
356 if parent not in seen:
359 if parent not in seen:
357 visit.append(parent)
360 visit.append(parent)
358 seen.add(parent)
361 seen.add(parent)
359 yield parent
362 yield parent
360
363
361 def incancestors(self, revs, stoprev=0):
364 def incancestors(self, revs, stoprev=0):
362 """Identical to ancestors() except it also generates the
365 """Identical to ancestors() except it also generates the
363 revisions, 'revs'"""
366 revisions, 'revs'"""
364 for rev in revs:
367 for rev in revs:
365 yield rev
368 yield rev
366 for rev in self.ancestors(revs, stoprev):
369 for rev in self.ancestors(revs, stoprev):
367 yield rev
370 yield rev
368
371
369 def descendants(self, revs):
372 def descendants(self, revs):
370 """Generate the descendants of 'revs' in revision order.
373 """Generate the descendants of 'revs' in revision order.
371
374
372 Yield a sequence of revision numbers starting with a child of
375 Yield a sequence of revision numbers starting with a child of
373 some rev in revs, i.e., each revision is *not* considered a
376 some rev in revs, i.e., each revision is *not* considered a
374 descendant of itself. Results are ordered by revision number (a
377 descendant of itself. Results are ordered by revision number (a
375 topological sort)."""
378 topological sort)."""
376 first = min(revs)
379 first = min(revs)
377 if first == nullrev:
380 if first == nullrev:
378 for i in self:
381 for i in self:
379 yield i
382 yield i
380 return
383 return
381
384
382 seen = set(revs)
385 seen = set(revs)
383 for i in self.revs(start=first + 1):
386 for i in self.revs(start=first + 1):
384 for x in self.parentrevs(i):
387 for x in self.parentrevs(i):
385 if x != nullrev and x in seen:
388 if x != nullrev and x in seen:
386 seen.add(i)
389 seen.add(i)
387 yield i
390 yield i
388 break
391 break
389
392
390 def findcommonmissing(self, common=None, heads=None):
393 def findcommonmissing(self, common=None, heads=None):
391 """Return a tuple of the ancestors of common and the ancestors of heads
394 """Return a tuple of the ancestors of common and the ancestors of heads
392 that are not ancestors of common. In revset terminology, we return the
395 that are not ancestors of common. In revset terminology, we return the
393 tuple:
396 tuple:
394
397
395 ::common, (::heads) - (::common)
398 ::common, (::heads) - (::common)
396
399
397 The list is sorted by revision number, meaning it is
400 The list is sorted by revision number, meaning it is
398 topologically sorted.
401 topologically sorted.
399
402
400 'heads' and 'common' are both lists of node IDs. If heads is
403 'heads' and 'common' are both lists of node IDs. If heads is
401 not supplied, uses all of the revlog's heads. If common is not
404 not supplied, uses all of the revlog's heads. If common is not
402 supplied, uses nullid."""
405 supplied, uses nullid."""
403 if common is None:
406 if common is None:
404 common = [nullid]
407 common = [nullid]
405 if heads is None:
408 if heads is None:
406 heads = self.heads()
409 heads = self.heads()
407
410
408 common = [self.rev(n) for n in common]
411 common = [self.rev(n) for n in common]
409 heads = [self.rev(n) for n in heads]
412 heads = [self.rev(n) for n in heads]
410
413
411 # we want the ancestors, but inclusive
414 # we want the ancestors, but inclusive
412 has = set(self.ancestors(common))
415 has = set(self.ancestors(common))
413 has.add(nullrev)
416 has.add(nullrev)
414 has.update(common)
417 has.update(common)
415
418
416 # take all ancestors from heads that aren't in has
419 # take all ancestors from heads that aren't in has
417 missing = set()
420 missing = set()
418 visit = util.deque(r for r in heads if r not in has)
421 visit = util.deque(r for r in heads if r not in has)
419 while visit:
422 while visit:
420 r = visit.popleft()
423 r = visit.popleft()
421 if r in missing:
424 if r in missing:
422 continue
425 continue
423 else:
426 else:
424 missing.add(r)
427 missing.add(r)
425 for p in self.parentrevs(r):
428 for p in self.parentrevs(r):
426 if p not in has:
429 if p not in has:
427 visit.append(p)
430 visit.append(p)
428 missing = list(missing)
431 missing = list(missing)
429 missing.sort()
432 missing.sort()
430 return has, [self.node(r) for r in missing]
433 return has, [self.node(r) for r in missing]
431
434
432 def findmissingrevs(self, common=None, heads=None):
435 def findmissingrevs(self, common=None, heads=None):
433 """Return the revision numbers of the ancestors of heads that
436 """Return the revision numbers of the ancestors of heads that
434 are not ancestors of common.
437 are not ancestors of common.
435
438
436 More specifically, return a list of revision numbers corresponding to
439 More specifically, return a list of revision numbers corresponding to
437 nodes N such that every N satisfies the following constraints:
440 nodes N such that every N satisfies the following constraints:
438
441
439 1. N is an ancestor of some node in 'heads'
442 1. N is an ancestor of some node in 'heads'
440 2. N is not an ancestor of any node in 'common'
443 2. N is not an ancestor of any node in 'common'
441
444
442 The list is sorted by revision number, meaning it is
445 The list is sorted by revision number, meaning it is
443 topologically sorted.
446 topologically sorted.
444
447
445 'heads' and 'common' are both lists of revision numbers. If heads is
448 'heads' and 'common' are both lists of revision numbers. If heads is
446 not supplied, uses all of the revlog's heads. If common is not
449 not supplied, uses all of the revlog's heads. If common is not
447 supplied, uses nullid."""
450 supplied, uses nullid."""
448 if common is None:
451 if common is None:
449 common = [nullrev]
452 common = [nullrev]
450 if heads is None:
453 if heads is None:
451 heads = self.headrevs()
454 heads = self.headrevs()
452
455
453 return ancestor.missingancestors(heads, common, self.parentrevs)
456 return ancestor.missingancestors(heads, common, self.parentrevs)
454
457
455 def findmissing(self, common=None, heads=None):
458 def findmissing(self, common=None, heads=None):
456 """Return the ancestors of heads that are not ancestors of common.
459 """Return the ancestors of heads that are not ancestors of common.
457
460
458 More specifically, return a list of nodes N such that every N
461 More specifically, return a list of nodes N such that every N
459 satisfies the following constraints:
462 satisfies the following constraints:
460
463
461 1. N is an ancestor of some node in 'heads'
464 1. N is an ancestor of some node in 'heads'
462 2. N is not an ancestor of any node in 'common'
465 2. N is not an ancestor of any node in 'common'
463
466
464 The list is sorted by revision number, meaning it is
467 The list is sorted by revision number, meaning it is
465 topologically sorted.
468 topologically sorted.
466
469
467 'heads' and 'common' are both lists of node IDs. If heads is
470 'heads' and 'common' are both lists of node IDs. If heads is
468 not supplied, uses all of the revlog's heads. If common is not
471 not supplied, uses all of the revlog's heads. If common is not
469 supplied, uses nullid."""
472 supplied, uses nullid."""
470 if common is None:
473 if common is None:
471 common = [nullid]
474 common = [nullid]
472 if heads is None:
475 if heads is None:
473 heads = self.heads()
476 heads = self.heads()
474
477
475 common = [self.rev(n) for n in common]
478 common = [self.rev(n) for n in common]
476 heads = [self.rev(n) for n in heads]
479 heads = [self.rev(n) for n in heads]
477
480
478 return [self.node(r) for r in
481 return [self.node(r) for r in
479 ancestor.missingancestors(heads, common, self.parentrevs)]
482 ancestor.missingancestors(heads, common, self.parentrevs)]
480
483
481 def nodesbetween(self, roots=None, heads=None):
484 def nodesbetween(self, roots=None, heads=None):
482 """Return a topological path from 'roots' to 'heads'.
485 """Return a topological path from 'roots' to 'heads'.
483
486
484 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
487 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
485 topologically sorted list of all nodes N that satisfy both of
488 topologically sorted list of all nodes N that satisfy both of
486 these constraints:
489 these constraints:
487
490
488 1. N is a descendant of some node in 'roots'
491 1. N is a descendant of some node in 'roots'
489 2. N is an ancestor of some node in 'heads'
492 2. N is an ancestor of some node in 'heads'
490
493
491 Every node is considered to be both a descendant and an ancestor
494 Every node is considered to be both a descendant and an ancestor
492 of itself, so every reachable node in 'roots' and 'heads' will be
495 of itself, so every reachable node in 'roots' and 'heads' will be
493 included in 'nodes'.
496 included in 'nodes'.
494
497
495 'outroots' is the list of reachable nodes in 'roots', i.e., the
498 'outroots' is the list of reachable nodes in 'roots', i.e., the
496 subset of 'roots' that is returned in 'nodes'. Likewise,
499 subset of 'roots' that is returned in 'nodes'. Likewise,
497 'outheads' is the subset of 'heads' that is also in 'nodes'.
500 'outheads' is the subset of 'heads' that is also in 'nodes'.
498
501
499 'roots' and 'heads' are both lists of node IDs. If 'roots' is
502 'roots' and 'heads' are both lists of node IDs. If 'roots' is
500 unspecified, uses nullid as the only root. If 'heads' is
503 unspecified, uses nullid as the only root. If 'heads' is
501 unspecified, uses list of all of the revlog's heads."""
504 unspecified, uses list of all of the revlog's heads."""
502 nonodes = ([], [], [])
505 nonodes = ([], [], [])
503 if roots is not None:
506 if roots is not None:
504 roots = list(roots)
507 roots = list(roots)
505 if not roots:
508 if not roots:
506 return nonodes
509 return nonodes
507 lowestrev = min([self.rev(n) for n in roots])
510 lowestrev = min([self.rev(n) for n in roots])
508 else:
511 else:
509 roots = [nullid] # Everybody's a descendant of nullid
512 roots = [nullid] # Everybody's a descendant of nullid
510 lowestrev = nullrev
513 lowestrev = nullrev
511 if (lowestrev == nullrev) and (heads is None):
514 if (lowestrev == nullrev) and (heads is None):
512 # We want _all_ the nodes!
515 # We want _all_ the nodes!
513 return ([self.node(r) for r in self], [nullid], list(self.heads()))
516 return ([self.node(r) for r in self], [nullid], list(self.heads()))
514 if heads is None:
517 if heads is None:
515 # All nodes are ancestors, so the latest ancestor is the last
518 # All nodes are ancestors, so the latest ancestor is the last
516 # node.
519 # node.
517 highestrev = len(self) - 1
520 highestrev = len(self) - 1
518 # Set ancestors to None to signal that every node is an ancestor.
521 # Set ancestors to None to signal that every node is an ancestor.
519 ancestors = None
522 ancestors = None
520 # Set heads to an empty dictionary for later discovery of heads
523 # Set heads to an empty dictionary for later discovery of heads
521 heads = {}
524 heads = {}
522 else:
525 else:
523 heads = list(heads)
526 heads = list(heads)
524 if not heads:
527 if not heads:
525 return nonodes
528 return nonodes
526 ancestors = set()
529 ancestors = set()
527 # Turn heads into a dictionary so we can remove 'fake' heads.
530 # Turn heads into a dictionary so we can remove 'fake' heads.
528 # Also, later we will be using it to filter out the heads we can't
531 # Also, later we will be using it to filter out the heads we can't
529 # find from roots.
532 # find from roots.
530 heads = dict.fromkeys(heads, False)
533 heads = dict.fromkeys(heads, False)
531 # Start at the top and keep marking parents until we're done.
534 # Start at the top and keep marking parents until we're done.
532 nodestotag = set(heads)
535 nodestotag = set(heads)
533 # Remember where the top was so we can use it as a limit later.
536 # Remember where the top was so we can use it as a limit later.
534 highestrev = max([self.rev(n) for n in nodestotag])
537 highestrev = max([self.rev(n) for n in nodestotag])
535 while nodestotag:
538 while nodestotag:
536 # grab a node to tag
539 # grab a node to tag
537 n = nodestotag.pop()
540 n = nodestotag.pop()
538 # Never tag nullid
541 # Never tag nullid
539 if n == nullid:
542 if n == nullid:
540 continue
543 continue
541 # A node's revision number represents its place in a
544 # A node's revision number represents its place in a
542 # topologically sorted list of nodes.
545 # topologically sorted list of nodes.
543 r = self.rev(n)
546 r = self.rev(n)
544 if r >= lowestrev:
547 if r >= lowestrev:
545 if n not in ancestors:
548 if n not in ancestors:
546 # If we are possibly a descendant of one of the roots
549 # If we are possibly a descendant of one of the roots
547 # and we haven't already been marked as an ancestor
550 # and we haven't already been marked as an ancestor
548 ancestors.add(n) # Mark as ancestor
551 ancestors.add(n) # Mark as ancestor
549 # Add non-nullid parents to list of nodes to tag.
552 # Add non-nullid parents to list of nodes to tag.
550 nodestotag.update([p for p in self.parents(n) if
553 nodestotag.update([p for p in self.parents(n) if
551 p != nullid])
554 p != nullid])
552 elif n in heads: # We've seen it before, is it a fake head?
555 elif n in heads: # We've seen it before, is it a fake head?
553 # So it is, real heads should not be the ancestors of
556 # So it is, real heads should not be the ancestors of
554 # any other heads.
557 # any other heads.
555 heads.pop(n)
558 heads.pop(n)
556 if not ancestors:
559 if not ancestors:
557 return nonodes
560 return nonodes
558 # Now that we have our set of ancestors, we want to remove any
561 # Now that we have our set of ancestors, we want to remove any
559 # roots that are not ancestors.
562 # roots that are not ancestors.
560
563
561 # If one of the roots was nullid, everything is included anyway.
564 # If one of the roots was nullid, everything is included anyway.
562 if lowestrev > nullrev:
565 if lowestrev > nullrev:
563 # But, since we weren't, let's recompute the lowest rev to not
566 # But, since we weren't, let's recompute the lowest rev to not
564 # include roots that aren't ancestors.
567 # include roots that aren't ancestors.
565
568
566 # Filter out roots that aren't ancestors of heads
569 # Filter out roots that aren't ancestors of heads
567 roots = [n for n in roots if n in ancestors]
570 roots = [n for n in roots if n in ancestors]
568 # Recompute the lowest revision
571 # Recompute the lowest revision
569 if roots:
572 if roots:
570 lowestrev = min([self.rev(n) for n in roots])
573 lowestrev = min([self.rev(n) for n in roots])
571 else:
574 else:
572 # No more roots? Return empty list
575 # No more roots? Return empty list
573 return nonodes
576 return nonodes
574 else:
577 else:
575 # We are descending from nullid, and don't need to care about
578 # We are descending from nullid, and don't need to care about
576 # any other roots.
579 # any other roots.
577 lowestrev = nullrev
580 lowestrev = nullrev
578 roots = [nullid]
581 roots = [nullid]
579 # Transform our roots list into a set.
582 # Transform our roots list into a set.
580 descendants = set(roots)
583 descendants = set(roots)
581 # Also, keep the original roots so we can filter out roots that aren't
584 # Also, keep the original roots so we can filter out roots that aren't
582 # 'real' roots (i.e. are descended from other roots).
585 # 'real' roots (i.e. are descended from other roots).
583 roots = descendants.copy()
586 roots = descendants.copy()
584 # Our topologically sorted list of output nodes.
587 # Our topologically sorted list of output nodes.
585 orderedout = []
588 orderedout = []
586 # Don't start at nullid since we don't want nullid in our output list,
589 # Don't start at nullid since we don't want nullid in our output list,
587 # and if nullid shows up in descendants, empty parents will look like
590 # and if nullid shows up in descendants, empty parents will look like
588 # they're descendants.
591 # they're descendants.
589 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
592 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
590 n = self.node(r)
593 n = self.node(r)
591 isdescendant = False
594 isdescendant = False
592 if lowestrev == nullrev: # Everybody is a descendant of nullid
595 if lowestrev == nullrev: # Everybody is a descendant of nullid
593 isdescendant = True
596 isdescendant = True
594 elif n in descendants:
597 elif n in descendants:
595 # n is already a descendant
598 # n is already a descendant
596 isdescendant = True
599 isdescendant = True
597 # This check only needs to be done here because all the roots
600 # This check only needs to be done here because all the roots
598 # will start being marked is descendants before the loop.
601 # will start being marked is descendants before the loop.
599 if n in roots:
602 if n in roots:
600 # If n was a root, check if it's a 'real' root.
603 # If n was a root, check if it's a 'real' root.
601 p = tuple(self.parents(n))
604 p = tuple(self.parents(n))
602 # If any of its parents are descendants, it's not a root.
605 # If any of its parents are descendants, it's not a root.
603 if (p[0] in descendants) or (p[1] in descendants):
606 if (p[0] in descendants) or (p[1] in descendants):
604 roots.remove(n)
607 roots.remove(n)
605 else:
608 else:
606 p = tuple(self.parents(n))
609 p = tuple(self.parents(n))
607 # A node is a descendant if either of its parents are
610 # A node is a descendant if either of its parents are
608 # descendants. (We seeded the dependents list with the roots
611 # descendants. (We seeded the dependents list with the roots
609 # up there, remember?)
612 # up there, remember?)
610 if (p[0] in descendants) or (p[1] in descendants):
613 if (p[0] in descendants) or (p[1] in descendants):
611 descendants.add(n)
614 descendants.add(n)
612 isdescendant = True
615 isdescendant = True
613 if isdescendant and ((ancestors is None) or (n in ancestors)):
616 if isdescendant and ((ancestors is None) or (n in ancestors)):
614 # Only include nodes that are both descendants and ancestors.
617 # Only include nodes that are both descendants and ancestors.
615 orderedout.append(n)
618 orderedout.append(n)
616 if (ancestors is not None) and (n in heads):
619 if (ancestors is not None) and (n in heads):
617 # We're trying to figure out which heads are reachable
620 # We're trying to figure out which heads are reachable
618 # from roots.
621 # from roots.
619 # Mark this head as having been reached
622 # Mark this head as having been reached
620 heads[n] = True
623 heads[n] = True
621 elif ancestors is None:
624 elif ancestors is None:
622 # Otherwise, we're trying to discover the heads.
625 # Otherwise, we're trying to discover the heads.
623 # Assume this is a head because if it isn't, the next step
626 # Assume this is a head because if it isn't, the next step
624 # will eventually remove it.
627 # will eventually remove it.
625 heads[n] = True
628 heads[n] = True
626 # But, obviously its parents aren't.
629 # But, obviously its parents aren't.
627 for p in self.parents(n):
630 for p in self.parents(n):
628 heads.pop(p, None)
631 heads.pop(p, None)
629 heads = [n for n, flag in heads.iteritems() if flag]
632 heads = [n for n, flag in heads.iteritems() if flag]
630 roots = list(roots)
633 roots = list(roots)
631 assert orderedout
634 assert orderedout
632 assert roots
635 assert roots
633 assert heads
636 assert heads
634 return (orderedout, roots, heads)
637 return (orderedout, roots, heads)
635
638
636 def headrevs(self):
639 def headrevs(self):
637 try:
640 try:
638 return self.index.headrevs()
641 return self.index.headrevs()
639 except AttributeError:
642 except AttributeError:
640 return self._headrevs()
643 return self._headrevs()
641
644
642 def _headrevs(self):
645 def _headrevs(self):
643 count = len(self)
646 count = len(self)
644 if not count:
647 if not count:
645 return [nullrev]
648 return [nullrev]
646 # we won't iter over filtered rev so nobody is a head at start
649 # we won't iter over filtered rev so nobody is a head at start
647 ishead = [0] * (count + 1)
650 ishead = [0] * (count + 1)
648 index = self.index
651 index = self.index
649 for r in self:
652 for r in self:
650 ishead[r] = 1 # I may be an head
653 ishead[r] = 1 # I may be an head
651 e = index[r]
654 e = index[r]
652 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
655 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
653 return [r for r, val in enumerate(ishead) if val]
656 return [r for r, val in enumerate(ishead) if val]
654
657
655 def heads(self, start=None, stop=None):
658 def heads(self, start=None, stop=None):
656 """return the list of all nodes that have no children
659 """return the list of all nodes that have no children
657
660
658 if start is specified, only heads that are descendants of
661 if start is specified, only heads that are descendants of
659 start will be returned
662 start will be returned
660 if stop is specified, it will consider all the revs from stop
663 if stop is specified, it will consider all the revs from stop
661 as if they had no children
664 as if they had no children
662 """
665 """
663 if start is None and stop is None:
666 if start is None and stop is None:
664 if not len(self):
667 if not len(self):
665 return [nullid]
668 return [nullid]
666 return [self.node(r) for r in self.headrevs()]
669 return [self.node(r) for r in self.headrevs()]
667
670
668 if start is None:
671 if start is None:
669 start = nullid
672 start = nullid
670 if stop is None:
673 if stop is None:
671 stop = []
674 stop = []
672 stoprevs = set([self.rev(n) for n in stop])
675 stoprevs = set([self.rev(n) for n in stop])
673 startrev = self.rev(start)
676 startrev = self.rev(start)
674 reachable = set((startrev,))
677 reachable = set((startrev,))
675 heads = set((startrev,))
678 heads = set((startrev,))
676
679
677 parentrevs = self.parentrevs
680 parentrevs = self.parentrevs
678 for r in self.revs(start=startrev + 1):
681 for r in self.revs(start=startrev + 1):
679 for p in parentrevs(r):
682 for p in parentrevs(r):
680 if p in reachable:
683 if p in reachable:
681 if r not in stoprevs:
684 if r not in stoprevs:
682 reachable.add(r)
685 reachable.add(r)
683 heads.add(r)
686 heads.add(r)
684 if p in heads and p not in stoprevs:
687 if p in heads and p not in stoprevs:
685 heads.remove(p)
688 heads.remove(p)
686
689
687 return [self.node(r) for r in heads]
690 return [self.node(r) for r in heads]
688
691
689 def children(self, node):
692 def children(self, node):
690 """find the children of a given node"""
693 """find the children of a given node"""
691 c = []
694 c = []
692 p = self.rev(node)
695 p = self.rev(node)
693 for r in self.revs(start=p + 1):
696 for r in self.revs(start=p + 1):
694 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
697 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
695 if prevs:
698 if prevs:
696 for pr in prevs:
699 for pr in prevs:
697 if pr == p:
700 if pr == p:
698 c.append(self.node(r))
701 c.append(self.node(r))
699 elif p == nullrev:
702 elif p == nullrev:
700 c.append(self.node(r))
703 c.append(self.node(r))
701 return c
704 return c
702
705
703 def descendant(self, start, end):
706 def descendant(self, start, end):
704 if start == nullrev:
707 if start == nullrev:
705 return True
708 return True
706 for i in self.descendants([start]):
709 for i in self.descendants([start]):
707 if i == end:
710 if i == end:
708 return True
711 return True
709 elif i > end:
712 elif i > end:
710 break
713 break
711 return False
714 return False
712
715
713 def ancestor(self, a, b):
716 def ancestor(self, a, b):
714 """calculate the least common ancestor of nodes a and b"""
717 """calculate the least common ancestor of nodes a and b"""
715
718
716 # fast path, check if it is a descendant
719 # fast path, check if it is a descendant
717 a, b = self.rev(a), self.rev(b)
720 a, b = self.rev(a), self.rev(b)
718 start, end = sorted((a, b))
721 start, end = sorted((a, b))
719 if self.descendant(start, end):
722 if self.descendant(start, end):
720 return self.node(start)
723 return self.node(start)
721
724
722 def parents(rev):
725 def parents(rev):
723 return [p for p in self.parentrevs(rev) if p != nullrev]
726 return [p for p in self.parentrevs(rev) if p != nullrev]
724
727
725 c = ancestor.ancestor(a, b, parents)
728 c = ancestor.ancestor(a, b, parents)
726 if c is None:
729 if c is None:
727 return nullid
730 return nullid
728
731
729 return self.node(c)
732 return self.node(c)
730
733
731 def _match(self, id):
734 def _match(self, id):
732 if isinstance(id, int):
735 if isinstance(id, int):
733 # rev
736 # rev
734 return self.node(id)
737 return self.node(id)
735 if len(id) == 20:
738 if len(id) == 20:
736 # possibly a binary node
739 # possibly a binary node
737 # odds of a binary node being all hex in ASCII are 1 in 10**25
740 # odds of a binary node being all hex in ASCII are 1 in 10**25
738 try:
741 try:
739 node = id
742 node = id
740 self.rev(node) # quick search the index
743 self.rev(node) # quick search the index
741 return node
744 return node
742 except LookupError:
745 except LookupError:
743 pass # may be partial hex id
746 pass # may be partial hex id
744 try:
747 try:
745 # str(rev)
748 # str(rev)
746 rev = int(id)
749 rev = int(id)
747 if str(rev) != id:
750 if str(rev) != id:
748 raise ValueError
751 raise ValueError
749 if rev < 0:
752 if rev < 0:
750 rev = len(self) + rev
753 rev = len(self) + rev
751 if rev < 0 or rev >= len(self):
754 if rev < 0 or rev >= len(self):
752 raise ValueError
755 raise ValueError
753 return self.node(rev)
756 return self.node(rev)
754 except (ValueError, OverflowError):
757 except (ValueError, OverflowError):
755 pass
758 pass
756 if len(id) == 40:
759 if len(id) == 40:
757 try:
760 try:
758 # a full hex nodeid?
761 # a full hex nodeid?
759 node = bin(id)
762 node = bin(id)
760 self.rev(node)
763 self.rev(node)
761 return node
764 return node
762 except (TypeError, LookupError):
765 except (TypeError, LookupError):
763 pass
766 pass
764
767
765 def _partialmatch(self, id):
768 def _partialmatch(self, id):
766 try:
769 try:
767 return self.index.partialmatch(id)
770 return self.index.partialmatch(id)
768 except RevlogError:
771 except RevlogError:
769 # parsers.c radix tree lookup gave multiple matches
772 # parsers.c radix tree lookup gave multiple matches
770 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
773 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
771 except (AttributeError, ValueError):
774 except (AttributeError, ValueError):
772 # we are pure python, or key was too short to search radix tree
775 # we are pure python, or key was too short to search radix tree
773 pass
776 pass
774
777
775 if id in self._pcache:
778 if id in self._pcache:
776 return self._pcache[id]
779 return self._pcache[id]
777
780
778 if len(id) < 40:
781 if len(id) < 40:
779 try:
782 try:
780 # hex(node)[:...]
783 # hex(node)[:...]
781 l = len(id) // 2 # grab an even number of digits
784 l = len(id) // 2 # grab an even number of digits
782 prefix = bin(id[:l * 2])
785 prefix = bin(id[:l * 2])
783 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
786 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
784 nl = [n for n in nl if hex(n).startswith(id)]
787 nl = [n for n in nl if hex(n).startswith(id)]
785 if len(nl) > 0:
788 if len(nl) > 0:
786 if len(nl) == 1:
789 if len(nl) == 1:
787 self._pcache[id] = nl[0]
790 self._pcache[id] = nl[0]
788 return nl[0]
791 return nl[0]
789 raise LookupError(id, self.indexfile,
792 raise LookupError(id, self.indexfile,
790 _('ambiguous identifier'))
793 _('ambiguous identifier'))
791 return None
794 return None
792 except TypeError:
795 except TypeError:
793 pass
796 pass
794
797
795 def lookup(self, id):
798 def lookup(self, id):
796 """locate a node based on:
799 """locate a node based on:
797 - revision number or str(revision number)
800 - revision number or str(revision number)
798 - nodeid or subset of hex nodeid
801 - nodeid or subset of hex nodeid
799 """
802 """
800 n = self._match(id)
803 n = self._match(id)
801 if n is not None:
804 if n is not None:
802 return n
805 return n
803 n = self._partialmatch(id)
806 n = self._partialmatch(id)
804 if n:
807 if n:
805 return n
808 return n
806
809
807 raise LookupError(id, self.indexfile, _('no match found'))
810 raise LookupError(id, self.indexfile, _('no match found'))
808
811
809 def cmp(self, node, text):
812 def cmp(self, node, text):
810 """compare text with a given file revision
813 """compare text with a given file revision
811
814
812 returns True if text is different than what is stored.
815 returns True if text is different than what is stored.
813 """
816 """
814 p1, p2 = self.parents(node)
817 p1, p2 = self.parents(node)
815 return hash(text, p1, p2) != node
818 return hash(text, p1, p2) != node
816
819
817 def _addchunk(self, offset, data):
820 def _addchunk(self, offset, data):
818 o, d = self._chunkcache
821 o, d = self._chunkcache
819 # try to add to existing cache
822 # try to add to existing cache
820 if o + len(d) == offset and len(d) + len(data) < _chunksize:
823 if o + len(d) == offset and len(d) + len(data) < _chunksize:
821 self._chunkcache = o, d + data
824 self._chunkcache = o, d + data
822 else:
825 else:
823 self._chunkcache = offset, data
826 self._chunkcache = offset, data
824
827
825 def _loadchunk(self, offset, length):
828 def _loadchunk(self, offset, length):
826 if self._inline:
829 if self._inline:
827 df = self.opener(self.indexfile)
830 df = self.opener(self.indexfile)
828 else:
831 else:
829 df = self.opener(self.datafile)
832 df = self.opener(self.datafile)
830
833
831 readahead = max(65536, length)
834 readahead = max(65536, length)
832 df.seek(offset)
835 df.seek(offset)
833 d = df.read(readahead)
836 d = df.read(readahead)
834 df.close()
837 df.close()
835 self._addchunk(offset, d)
838 self._addchunk(offset, d)
836 if readahead > length:
839 if readahead > length:
837 return util.buffer(d, 0, length)
840 return util.buffer(d, 0, length)
838 return d
841 return d
839
842
840 def _getchunk(self, offset, length):
843 def _getchunk(self, offset, length):
841 o, d = self._chunkcache
844 o, d = self._chunkcache
842 l = len(d)
845 l = len(d)
843
846
844 # is it in the cache?
847 # is it in the cache?
845 cachestart = offset - o
848 cachestart = offset - o
846 cacheend = cachestart + length
849 cacheend = cachestart + length
847 if cachestart >= 0 and cacheend <= l:
850 if cachestart >= 0 and cacheend <= l:
848 if cachestart == 0 and cacheend == l:
851 if cachestart == 0 and cacheend == l:
849 return d # avoid a copy
852 return d # avoid a copy
850 return util.buffer(d, cachestart, cacheend - cachestart)
853 return util.buffer(d, cachestart, cacheend - cachestart)
851
854
852 return self._loadchunk(offset, length)
855 return self._loadchunk(offset, length)
853
856
854 def _chunkraw(self, startrev, endrev):
857 def _chunkraw(self, startrev, endrev):
855 start = self.start(startrev)
858 start = self.start(startrev)
856 length = self.end(endrev) - start
859 length = self.end(endrev) - start
857 if self._inline:
860 if self._inline:
858 start += (startrev + 1) * self._io.size
861 start += (startrev + 1) * self._io.size
859 return self._getchunk(start, length)
862 return self._getchunk(start, length)
860
863
861 def _chunk(self, rev):
864 def _chunk(self, rev):
862 return decompress(self._chunkraw(rev, rev))
865 return decompress(self._chunkraw(rev, rev))
863
866
864 def _chunkbase(self, rev):
867 def _chunkbase(self, rev):
865 return self._chunk(rev)
868 return self._chunk(rev)
866
869
867 def _chunkclear(self):
870 def _chunkclear(self):
868 self._chunkcache = (0, '')
871 self._chunkcache = (0, '')
869
872
870 def deltaparent(self, rev):
873 def deltaparent(self, rev):
871 """return deltaparent of the given revision"""
874 """return deltaparent of the given revision"""
872 base = self.index[rev][3]
875 base = self.index[rev][3]
873 if base == rev:
876 if base == rev:
874 return nullrev
877 return nullrev
875 elif self._generaldelta:
878 elif self._generaldelta:
876 return base
879 return base
877 else:
880 else:
878 return rev - 1
881 return rev - 1
879
882
880 def revdiff(self, rev1, rev2):
883 def revdiff(self, rev1, rev2):
881 """return or calculate a delta between two revisions"""
884 """return or calculate a delta between two revisions"""
882 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
885 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
883 return str(self._chunk(rev2))
886 return str(self._chunk(rev2))
884
887
885 return mdiff.textdiff(self.revision(rev1),
888 return mdiff.textdiff(self.revision(rev1),
886 self.revision(rev2))
889 self.revision(rev2))
887
890
888 def revision(self, nodeorrev):
891 def revision(self, nodeorrev):
889 """return an uncompressed revision of a given node or revision
892 """return an uncompressed revision of a given node or revision
890 number.
893 number.
891 """
894 """
892 if isinstance(nodeorrev, int):
895 if isinstance(nodeorrev, int):
893 rev = nodeorrev
896 rev = nodeorrev
894 node = self.node(rev)
897 node = self.node(rev)
895 else:
898 else:
896 node = nodeorrev
899 node = nodeorrev
897 rev = None
900 rev = None
898
901
899 cachedrev = None
902 cachedrev = None
900 if node == nullid:
903 if node == nullid:
901 return ""
904 return ""
902 if self._cache:
905 if self._cache:
903 if self._cache[0] == node:
906 if self._cache[0] == node:
904 return self._cache[2]
907 return self._cache[2]
905 cachedrev = self._cache[1]
908 cachedrev = self._cache[1]
906
909
907 # look up what we need to read
910 # look up what we need to read
908 text = None
911 text = None
909 if rev is None:
912 if rev is None:
910 rev = self.rev(node)
913 rev = self.rev(node)
911
914
912 # check rev flags
915 # check rev flags
913 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
916 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
914 raise RevlogError(_('incompatible revision flag %x') %
917 raise RevlogError(_('incompatible revision flag %x') %
915 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
918 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
916
919
917 # build delta chain
920 # build delta chain
918 chain = []
921 chain = []
919 index = self.index # for performance
922 index = self.index # for performance
920 generaldelta = self._generaldelta
923 generaldelta = self._generaldelta
921 iterrev = rev
924 iterrev = rev
922 e = index[iterrev]
925 e = index[iterrev]
923 while iterrev != e[3] and iterrev != cachedrev:
926 while iterrev != e[3] and iterrev != cachedrev:
924 chain.append(iterrev)
927 chain.append(iterrev)
925 if generaldelta:
928 if generaldelta:
926 iterrev = e[3]
929 iterrev = e[3]
927 else:
930 else:
928 iterrev -= 1
931 iterrev -= 1
929 e = index[iterrev]
932 e = index[iterrev]
930 chain.reverse()
933 chain.reverse()
931 base = iterrev
934 base = iterrev
932
935
933 if iterrev == cachedrev:
936 if iterrev == cachedrev:
934 # cache hit
937 # cache hit
935 text = self._cache[2]
938 text = self._cache[2]
936
939
937 # drop cache to save memory
940 # drop cache to save memory
938 self._cache = None
941 self._cache = None
939
942
940 self._chunkraw(base, rev)
943 self._chunkraw(base, rev)
941 if text is None:
944 if text is None:
942 text = str(self._chunkbase(base))
945 text = str(self._chunkbase(base))
943
946
944 bins = [self._chunk(r) for r in chain]
947 bins = [self._chunk(r) for r in chain]
945 text = mdiff.patches(text, bins)
948 text = mdiff.patches(text, bins)
946
949
947 text = self._checkhash(text, node, rev)
950 text = self._checkhash(text, node, rev)
948
951
949 self._cache = (node, rev, text)
952 self._cache = (node, rev, text)
950 return text
953 return text
951
954
952 def _checkhash(self, text, node, rev):
955 def _checkhash(self, text, node, rev):
953 p1, p2 = self.parents(node)
956 p1, p2 = self.parents(node)
954 if node != hash(text, p1, p2):
957 if node != hash(text, p1, p2):
955 raise RevlogError(_("integrity check failed on %s:%d")
958 raise RevlogError(_("integrity check failed on %s:%d")
956 % (self.indexfile, rev))
959 % (self.indexfile, rev))
957 return text
960 return text
958
961
959 def checkinlinesize(self, tr, fp=None):
962 def checkinlinesize(self, tr, fp=None):
960 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
963 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
961 return
964 return
962
965
963 trinfo = tr.find(self.indexfile)
966 trinfo = tr.find(self.indexfile)
964 if trinfo is None:
967 if trinfo is None:
965 raise RevlogError(_("%s not found in the transaction")
968 raise RevlogError(_("%s not found in the transaction")
966 % self.indexfile)
969 % self.indexfile)
967
970
968 trindex = trinfo[2]
971 trindex = trinfo[2]
969 dataoff = self.start(trindex)
972 dataoff = self.start(trindex)
970
973
971 tr.add(self.datafile, dataoff)
974 tr.add(self.datafile, dataoff)
972
975
973 if fp:
976 if fp:
974 fp.flush()
977 fp.flush()
975 fp.close()
978 fp.close()
976
979
977 df = self.opener(self.datafile, 'w')
980 df = self.opener(self.datafile, 'w')
978 try:
981 try:
979 for r in self:
982 for r in self:
980 df.write(self._chunkraw(r, r))
983 df.write(self._chunkraw(r, r))
981 finally:
984 finally:
982 df.close()
985 df.close()
983
986
984 fp = self.opener(self.indexfile, 'w', atomictemp=True)
987 fp = self.opener(self.indexfile, 'w', atomictemp=True)
985 self.version &= ~(REVLOGNGINLINEDATA)
988 self.version &= ~(REVLOGNGINLINEDATA)
986 self._inline = False
989 self._inline = False
987 for i in self:
990 for i in self:
988 e = self._io.packentry(self.index[i], self.node, self.version, i)
991 e = self._io.packentry(self.index[i], self.node, self.version, i)
989 fp.write(e)
992 fp.write(e)
990
993
991 # if we don't call close, the temp file will never replace the
994 # if we don't call close, the temp file will never replace the
992 # real index
995 # real index
993 fp.close()
996 fp.close()
994
997
995 tr.replace(self.indexfile, trindex * self._io.size)
998 tr.replace(self.indexfile, trindex * self._io.size)
996 self._chunkclear()
999 self._chunkclear()
997
1000
998 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
1001 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
999 """add a revision to the log
1002 """add a revision to the log
1000
1003
1001 text - the revision data to add
1004 text - the revision data to add
1002 transaction - the transaction object used for rollback
1005 transaction - the transaction object used for rollback
1003 link - the linkrev data to add
1006 link - the linkrev data to add
1004 p1, p2 - the parent nodeids of the revision
1007 p1, p2 - the parent nodeids of the revision
1005 cachedelta - an optional precomputed delta
1008 cachedelta - an optional precomputed delta
1006 """
1009 """
1007 node = hash(text, p1, p2)
1010 node = hash(text, p1, p2)
1008 if node in self.nodemap:
1011 if node in self.nodemap:
1009 return node
1012 return node
1010
1013
1011 dfh = None
1014 dfh = None
1012 if not self._inline:
1015 if not self._inline:
1013 dfh = self.opener(self.datafile, "a")
1016 dfh = self.opener(self.datafile, "a")
1014 ifh = self.opener(self.indexfile, "a+")
1017 ifh = self.opener(self.indexfile, "a+")
1015 try:
1018 try:
1016 return self._addrevision(node, text, transaction, link, p1, p2,
1019 return self._addrevision(node, text, transaction, link, p1, p2,
1017 cachedelta, ifh, dfh)
1020 cachedelta, ifh, dfh)
1018 finally:
1021 finally:
1019 if dfh:
1022 if dfh:
1020 dfh.close()
1023 dfh.close()
1021 ifh.close()
1024 ifh.close()
1022
1025
1023 def compress(self, text):
1026 def compress(self, text):
1024 """ generate a possibly-compressed representation of text """
1027 """ generate a possibly-compressed representation of text """
1025 if not text:
1028 if not text:
1026 return ("", text)
1029 return ("", text)
1027 l = len(text)
1030 l = len(text)
1028 bin = None
1031 bin = None
1029 if l < 44:
1032 if l < 44:
1030 pass
1033 pass
1031 elif l > 1000000:
1034 elif l > 1000000:
1032 # zlib makes an internal copy, thus doubling memory usage for
1035 # zlib makes an internal copy, thus doubling memory usage for
1033 # large files, so lets do this in pieces
1036 # large files, so lets do this in pieces
1034 z = zlib.compressobj()
1037 z = zlib.compressobj()
1035 p = []
1038 p = []
1036 pos = 0
1039 pos = 0
1037 while pos < l:
1040 while pos < l:
1038 pos2 = pos + 2**20
1041 pos2 = pos + 2**20
1039 p.append(z.compress(text[pos:pos2]))
1042 p.append(z.compress(text[pos:pos2]))
1040 pos = pos2
1043 pos = pos2
1041 p.append(z.flush())
1044 p.append(z.flush())
1042 if sum(map(len, p)) < l:
1045 if sum(map(len, p)) < l:
1043 bin = "".join(p)
1046 bin = "".join(p)
1044 else:
1047 else:
1045 bin = _compress(text)
1048 bin = _compress(text)
1046 if bin is None or len(bin) > l:
1049 if bin is None or len(bin) > l:
1047 if text[0] == '\0':
1050 if text[0] == '\0':
1048 return ("", text)
1051 return ("", text)
1049 return ('u', text)
1052 return ('u', text)
1050 return ("", bin)
1053 return ("", bin)
1051
1054
1052 def _addrevision(self, node, text, transaction, link, p1, p2,
1055 def _addrevision(self, node, text, transaction, link, p1, p2,
1053 cachedelta, ifh, dfh):
1056 cachedelta, ifh, dfh):
1054 """internal function to add revisions to the log
1057 """internal function to add revisions to the log
1055
1058
1056 see addrevision for argument descriptions.
1059 see addrevision for argument descriptions.
1057 invariants:
1060 invariants:
1058 - text is optional (can be None); if not set, cachedelta must be set.
1061 - text is optional (can be None); if not set, cachedelta must be set.
1059 if both are set, they must correspond to each other.
1062 if both are set, they must correspond to each other.
1060 """
1063 """
1061 btext = [text]
1064 btext = [text]
1062 def buildtext():
1065 def buildtext():
1063 if btext[0] is not None:
1066 if btext[0] is not None:
1064 return btext[0]
1067 return btext[0]
1065 # flush any pending writes here so we can read it in revision
1068 # flush any pending writes here so we can read it in revision
1066 if dfh:
1069 if dfh:
1067 dfh.flush()
1070 dfh.flush()
1068 ifh.flush()
1071 ifh.flush()
1069 basetext = self.revision(self.node(cachedelta[0]))
1072 basetext = self.revision(self.node(cachedelta[0]))
1070 btext[0] = mdiff.patch(basetext, cachedelta[1])
1073 btext[0] = mdiff.patch(basetext, cachedelta[1])
1071 chk = hash(btext[0], p1, p2)
1074 chk = hash(btext[0], p1, p2)
1072 if chk != node:
1075 if chk != node:
1073 raise RevlogError(_("consistency error in delta"))
1076 raise RevlogError(_("consistency error in delta"))
1074 return btext[0]
1077 return btext[0]
1075
1078
1076 def builddelta(rev):
1079 def builddelta(rev):
1077 # can we use the cached delta?
1080 # can we use the cached delta?
1078 if cachedelta and cachedelta[0] == rev:
1081 if cachedelta and cachedelta[0] == rev:
1079 delta = cachedelta[1]
1082 delta = cachedelta[1]
1080 else:
1083 else:
1081 t = buildtext()
1084 t = buildtext()
1082 ptext = self.revision(self.node(rev))
1085 ptext = self.revision(self.node(rev))
1083 delta = mdiff.textdiff(ptext, t)
1086 delta = mdiff.textdiff(ptext, t)
1084 data = self.compress(delta)
1087 data = self.compress(delta)
1085 l = len(data[1]) + len(data[0])
1088 l = len(data[1]) + len(data[0])
1086 if basecache[0] == rev:
1089 if basecache[0] == rev:
1087 chainbase = basecache[1]
1090 chainbase = basecache[1]
1088 else:
1091 else:
1089 chainbase = self.chainbase(rev)
1092 chainbase = self.chainbase(rev)
1090 dist = l + offset - self.start(chainbase)
1093 dist = l + offset - self.start(chainbase)
1091 if self._generaldelta:
1094 if self._generaldelta:
1092 base = rev
1095 base = rev
1093 else:
1096 else:
1094 base = chainbase
1097 base = chainbase
1095 return dist, l, data, base, chainbase
1098 return dist, l, data, base, chainbase
1096
1099
1097 curr = len(self)
1100 curr = len(self)
1098 prev = curr - 1
1101 prev = curr - 1
1099 base = chainbase = curr
1102 base = chainbase = curr
1100 offset = self.end(prev)
1103 offset = self.end(prev)
1101 flags = 0
1104 flags = 0
1102 d = None
1105 d = None
1103 basecache = self._basecache
1106 basecache = self._basecache
1104 p1r, p2r = self.rev(p1), self.rev(p2)
1107 p1r, p2r = self.rev(p1), self.rev(p2)
1105
1108
1106 # should we try to build a delta?
1109 # should we try to build a delta?
1107 if prev != nullrev:
1110 if prev != nullrev:
1108 if self._generaldelta:
1111 if self._generaldelta:
1109 if p1r >= basecache[1]:
1112 if p1r >= basecache[1]:
1110 d = builddelta(p1r)
1113 d = builddelta(p1r)
1111 elif p2r >= basecache[1]:
1114 elif p2r >= basecache[1]:
1112 d = builddelta(p2r)
1115 d = builddelta(p2r)
1113 else:
1116 else:
1114 d = builddelta(prev)
1117 d = builddelta(prev)
1115 else:
1118 else:
1116 d = builddelta(prev)
1119 d = builddelta(prev)
1117 dist, l, data, base, chainbase = d
1120 dist, l, data, base, chainbase = d
1118
1121
1119 # full versions are inserted when the needed deltas
1122 # full versions are inserted when the needed deltas
1120 # become comparable to the uncompressed text
1123 # become comparable to the uncompressed text
1121 if text is None:
1124 if text is None:
1122 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1125 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1123 cachedelta[1])
1126 cachedelta[1])
1124 else:
1127 else:
1125 textlen = len(text)
1128 textlen = len(text)
1126 if d is None or dist > textlen * 2:
1129 if d is None or dist > textlen * 2:
1127 text = buildtext()
1130 text = buildtext()
1128 data = self.compress(text)
1131 data = self.compress(text)
1129 l = len(data[1]) + len(data[0])
1132 l = len(data[1]) + len(data[0])
1130 base = chainbase = curr
1133 base = chainbase = curr
1131
1134
1132 e = (offset_type(offset, flags), l, textlen,
1135 e = (offset_type(offset, flags), l, textlen,
1133 base, link, p1r, p2r, node)
1136 base, link, p1r, p2r, node)
1134 self.index.insert(-1, e)
1137 self.index.insert(-1, e)
1135 self.nodemap[node] = curr
1138 self.nodemap[node] = curr
1136
1139
1137 entry = self._io.packentry(e, self.node, self.version, curr)
1140 entry = self._io.packentry(e, self.node, self.version, curr)
1138 if not self._inline:
1141 if not self._inline:
1139 transaction.add(self.datafile, offset)
1142 transaction.add(self.datafile, offset)
1140 transaction.add(self.indexfile, curr * len(entry))
1143 transaction.add(self.indexfile, curr * len(entry))
1141 if data[0]:
1144 if data[0]:
1142 dfh.write(data[0])
1145 dfh.write(data[0])
1143 dfh.write(data[1])
1146 dfh.write(data[1])
1144 dfh.flush()
1147 dfh.flush()
1145 ifh.write(entry)
1148 ifh.write(entry)
1146 else:
1149 else:
1147 offset += curr * self._io.size
1150 offset += curr * self._io.size
1148 transaction.add(self.indexfile, offset, curr)
1151 transaction.add(self.indexfile, offset, curr)
1149 ifh.write(entry)
1152 ifh.write(entry)
1150 ifh.write(data[0])
1153 ifh.write(data[0])
1151 ifh.write(data[1])
1154 ifh.write(data[1])
1152 self.checkinlinesize(transaction, ifh)
1155 self.checkinlinesize(transaction, ifh)
1153
1156
1154 if type(text) == str: # only accept immutable objects
1157 if type(text) == str: # only accept immutable objects
1155 self._cache = (node, curr, text)
1158 self._cache = (node, curr, text)
1156 self._basecache = (curr, chainbase)
1159 self._basecache = (curr, chainbase)
1157 return node
1160 return node
1158
1161
1159 def group(self, nodelist, bundler, reorder=None):
1162 def group(self, nodelist, bundler, reorder=None):
1160 """Calculate a delta group, yielding a sequence of changegroup chunks
1163 """Calculate a delta group, yielding a sequence of changegroup chunks
1161 (strings).
1164 (strings).
1162
1165
1163 Given a list of changeset revs, return a set of deltas and
1166 Given a list of changeset revs, return a set of deltas and
1164 metadata corresponding to nodes. The first delta is
1167 metadata corresponding to nodes. The first delta is
1165 first parent(nodelist[0]) -> nodelist[0], the receiver is
1168 first parent(nodelist[0]) -> nodelist[0], the receiver is
1166 guaranteed to have this parent as it has all history before
1169 guaranteed to have this parent as it has all history before
1167 these changesets. In the case firstparent is nullrev the
1170 these changesets. In the case firstparent is nullrev the
1168 changegroup starts with a full revision.
1171 changegroup starts with a full revision.
1169 """
1172 """
1170
1173
1171 # if we don't have any revisions touched by these changesets, bail
1174 # if we don't have any revisions touched by these changesets, bail
1172 if len(nodelist) == 0:
1175 if len(nodelist) == 0:
1173 yield bundler.close()
1176 yield bundler.close()
1174 return
1177 return
1175
1178
1176 # for generaldelta revlogs, we linearize the revs; this will both be
1179 # for generaldelta revlogs, we linearize the revs; this will both be
1177 # much quicker and generate a much smaller bundle
1180 # much quicker and generate a much smaller bundle
1178 if (self._generaldelta and reorder is not False) or reorder:
1181 if (self._generaldelta and reorder is not False) or reorder:
1179 dag = dagutil.revlogdag(self)
1182 dag = dagutil.revlogdag(self)
1180 revs = set(self.rev(n) for n in nodelist)
1183 revs = set(self.rev(n) for n in nodelist)
1181 revs = dag.linearize(revs)
1184 revs = dag.linearize(revs)
1182 else:
1185 else:
1183 revs = sorted([self.rev(n) for n in nodelist])
1186 revs = sorted([self.rev(n) for n in nodelist])
1184
1187
1185 # add the parent of the first rev
1188 # add the parent of the first rev
1186 p = self.parentrevs(revs[0])[0]
1189 p = self.parentrevs(revs[0])[0]
1187 revs.insert(0, p)
1190 revs.insert(0, p)
1188
1191
1189 # build deltas
1192 # build deltas
1190 for r in xrange(len(revs) - 1):
1193 for r in xrange(len(revs) - 1):
1191 prev, curr = revs[r], revs[r + 1]
1194 prev, curr = revs[r], revs[r + 1]
1192 for c in bundler.revchunk(self, curr, prev):
1195 for c in bundler.revchunk(self, curr, prev):
1193 yield c
1196 yield c
1194
1197
1195 yield bundler.close()
1198 yield bundler.close()
1196
1199
1197 def addgroup(self, bundle, linkmapper, transaction):
1200 def addgroup(self, bundle, linkmapper, transaction):
1198 """
1201 """
1199 add a delta group
1202 add a delta group
1200
1203
1201 given a set of deltas, add them to the revision log. the
1204 given a set of deltas, add them to the revision log. the
1202 first delta is against its parent, which should be in our
1205 first delta is against its parent, which should be in our
1203 log, the rest are against the previous delta.
1206 log, the rest are against the previous delta.
1204 """
1207 """
1205
1208
1206 # track the base of the current delta log
1209 # track the base of the current delta log
1207 content = []
1210 content = []
1208 node = None
1211 node = None
1209
1212
1210 r = len(self)
1213 r = len(self)
1211 end = 0
1214 end = 0
1212 if r:
1215 if r:
1213 end = self.end(r - 1)
1216 end = self.end(r - 1)
1214 ifh = self.opener(self.indexfile, "a+")
1217 ifh = self.opener(self.indexfile, "a+")
1215 isize = r * self._io.size
1218 isize = r * self._io.size
1216 if self._inline:
1219 if self._inline:
1217 transaction.add(self.indexfile, end + isize, r)
1220 transaction.add(self.indexfile, end + isize, r)
1218 dfh = None
1221 dfh = None
1219 else:
1222 else:
1220 transaction.add(self.indexfile, isize, r)
1223 transaction.add(self.indexfile, isize, r)
1221 transaction.add(self.datafile, end)
1224 transaction.add(self.datafile, end)
1222 dfh = self.opener(self.datafile, "a")
1225 dfh = self.opener(self.datafile, "a")
1223
1226
1224 try:
1227 try:
1225 # loop through our set of deltas
1228 # loop through our set of deltas
1226 chain = None
1229 chain = None
1227 while True:
1230 while True:
1228 chunkdata = bundle.deltachunk(chain)
1231 chunkdata = bundle.deltachunk(chain)
1229 if not chunkdata:
1232 if not chunkdata:
1230 break
1233 break
1231 node = chunkdata['node']
1234 node = chunkdata['node']
1232 p1 = chunkdata['p1']
1235 p1 = chunkdata['p1']
1233 p2 = chunkdata['p2']
1236 p2 = chunkdata['p2']
1234 cs = chunkdata['cs']
1237 cs = chunkdata['cs']
1235 deltabase = chunkdata['deltabase']
1238 deltabase = chunkdata['deltabase']
1236 delta = chunkdata['delta']
1239 delta = chunkdata['delta']
1237
1240
1238 content.append(node)
1241 content.append(node)
1239
1242
1240 link = linkmapper(cs)
1243 link = linkmapper(cs)
1241 if node in self.nodemap:
1244 if node in self.nodemap:
1242 # this can happen if two branches make the same change
1245 # this can happen if two branches make the same change
1243 chain = node
1246 chain = node
1244 continue
1247 continue
1245
1248
1246 for p in (p1, p2):
1249 for p in (p1, p2):
1247 if p not in self.nodemap:
1250 if p not in self.nodemap:
1248 raise LookupError(p, self.indexfile,
1251 raise LookupError(p, self.indexfile,
1249 _('unknown parent'))
1252 _('unknown parent'))
1250
1253
1251 if deltabase not in self.nodemap:
1254 if deltabase not in self.nodemap:
1252 raise LookupError(deltabase, self.indexfile,
1255 raise LookupError(deltabase, self.indexfile,
1253 _('unknown delta base'))
1256 _('unknown delta base'))
1254
1257
1255 baserev = self.rev(deltabase)
1258 baserev = self.rev(deltabase)
1256 chain = self._addrevision(node, None, transaction, link,
1259 chain = self._addrevision(node, None, transaction, link,
1257 p1, p2, (baserev, delta), ifh, dfh)
1260 p1, p2, (baserev, delta), ifh, dfh)
1258 if not dfh and not self._inline:
1261 if not dfh and not self._inline:
1259 # addrevision switched from inline to conventional
1262 # addrevision switched from inline to conventional
1260 # reopen the index
1263 # reopen the index
1261 ifh.close()
1264 ifh.close()
1262 dfh = self.opener(self.datafile, "a")
1265 dfh = self.opener(self.datafile, "a")
1263 ifh = self.opener(self.indexfile, "a")
1266 ifh = self.opener(self.indexfile, "a")
1264 finally:
1267 finally:
1265 if dfh:
1268 if dfh:
1266 dfh.close()
1269 dfh.close()
1267 ifh.close()
1270 ifh.close()
1268
1271
1269 return content
1272 return content
1270
1273
1271 def strip(self, minlink, transaction):
1274 def strip(self, minlink, transaction):
1272 """truncate the revlog on the first revision with a linkrev >= minlink
1275 """truncate the revlog on the first revision with a linkrev >= minlink
1273
1276
1274 This function is called when we're stripping revision minlink and
1277 This function is called when we're stripping revision minlink and
1275 its descendants from the repository.
1278 its descendants from the repository.
1276
1279
1277 We have to remove all revisions with linkrev >= minlink, because
1280 We have to remove all revisions with linkrev >= minlink, because
1278 the equivalent changelog revisions will be renumbered after the
1281 the equivalent changelog revisions will be renumbered after the
1279 strip.
1282 strip.
1280
1283
1281 So we truncate the revlog on the first of these revisions, and
1284 So we truncate the revlog on the first of these revisions, and
1282 trust that the caller has saved the revisions that shouldn't be
1285 trust that the caller has saved the revisions that shouldn't be
1283 removed and that it'll re-add them after this truncation.
1286 removed and that it'll re-add them after this truncation.
1284 """
1287 """
1285 if len(self) == 0:
1288 if len(self) == 0:
1286 return
1289 return
1287
1290
1288 for rev in self:
1291 for rev in self:
1289 if self.index[rev][4] >= minlink:
1292 if self.index[rev][4] >= minlink:
1290 break
1293 break
1291 else:
1294 else:
1292 return
1295 return
1293
1296
1294 # first truncate the files on disk
1297 # first truncate the files on disk
1295 end = self.start(rev)
1298 end = self.start(rev)
1296 if not self._inline:
1299 if not self._inline:
1297 transaction.add(self.datafile, end)
1300 transaction.add(self.datafile, end)
1298 end = rev * self._io.size
1301 end = rev * self._io.size
1299 else:
1302 else:
1300 end += rev * self._io.size
1303 end += rev * self._io.size
1301
1304
1302 transaction.add(self.indexfile, end)
1305 transaction.add(self.indexfile, end)
1303
1306
1304 # then reset internal state in memory to forget those revisions
1307 # then reset internal state in memory to forget those revisions
1305 self._cache = None
1308 self._cache = None
1306 self._chunkclear()
1309 self._chunkclear()
1307 for x in xrange(rev, len(self)):
1310 for x in xrange(rev, len(self)):
1308 del self.nodemap[self.node(x)]
1311 del self.nodemap[self.node(x)]
1309
1312
1310 del self.index[rev:-1]
1313 del self.index[rev:-1]
1311
1314
1312 def checksize(self):
1315 def checksize(self):
1313 expected = 0
1316 expected = 0
1314 if len(self):
1317 if len(self):
1315 expected = max(0, self.end(len(self) - 1))
1318 expected = max(0, self.end(len(self) - 1))
1316
1319
1317 try:
1320 try:
1318 f = self.opener(self.datafile)
1321 f = self.opener(self.datafile)
1319 f.seek(0, 2)
1322 f.seek(0, 2)
1320 actual = f.tell()
1323 actual = f.tell()
1321 f.close()
1324 f.close()
1322 dd = actual - expected
1325 dd = actual - expected
1323 except IOError, inst:
1326 except IOError, inst:
1324 if inst.errno != errno.ENOENT:
1327 if inst.errno != errno.ENOENT:
1325 raise
1328 raise
1326 dd = 0
1329 dd = 0
1327
1330
1328 try:
1331 try:
1329 f = self.opener(self.indexfile)
1332 f = self.opener(self.indexfile)
1330 f.seek(0, 2)
1333 f.seek(0, 2)
1331 actual = f.tell()
1334 actual = f.tell()
1332 f.close()
1335 f.close()
1333 s = self._io.size
1336 s = self._io.size
1334 i = max(0, actual // s)
1337 i = max(0, actual // s)
1335 di = actual - (i * s)
1338 di = actual - (i * s)
1336 if self._inline:
1339 if self._inline:
1337 databytes = 0
1340 databytes = 0
1338 for r in self:
1341 for r in self:
1339 databytes += max(0, self.length(r))
1342 databytes += max(0, self.length(r))
1340 dd = 0
1343 dd = 0
1341 di = actual - len(self) * s - databytes
1344 di = actual - len(self) * s - databytes
1342 except IOError, inst:
1345 except IOError, inst:
1343 if inst.errno != errno.ENOENT:
1346 if inst.errno != errno.ENOENT:
1344 raise
1347 raise
1345 di = 0
1348 di = 0
1346
1349
1347 return (dd, di)
1350 return (dd, di)
1348
1351
1349 def files(self):
1352 def files(self):
1350 res = [self.indexfile]
1353 res = [self.indexfile]
1351 if not self._inline:
1354 if not self._inline:
1352 res.append(self.datafile)
1355 res.append(self.datafile)
1353 return res
1356 return res
General Comments 0
You need to be logged in to leave comments. Login now