mercurial/revlog.py
changeset 1680 c21b54f7f7b8
parent 1678 b345cc4c22c0
parent 1667 daff3ef0de8d
equal deleted inserted replaced
1679:675ca845c2f8 1680:c21b54f7f7b8
   186         """
   186         """
   187         self.indexfile = indexfile
   187         self.indexfile = indexfile
   188         self.datafile = datafile
   188         self.datafile = datafile
   189         self.opener = opener
   189         self.opener = opener
   190         self.cache = None
   190         self.cache = None
       
   191         self.chunkcache = None
   191 
   192 
   192         try:
   193         try:
   193             i = self.opener(self.indexfile).read()
   194             i = self.opener(self.indexfile).read()
   194         except IOError, inst:
   195         except IOError, inst:
   195             if inst.errno != errno.ENOENT:
   196             if inst.errno != errno.ENOENT:
   196                 raise
   197                 raise
   197             i = ""
   198             i = ""
       
   199 
       
   200         if i and i[:4] != "\0\0\0\0":
       
   201             raise RevlogError(_("incompatible revlog signature on %s") %
       
   202                               self.indexfile)
   198 
   203 
   199         if len(i) > 10000:
   204         if len(i) > 10000:
   200             # big index, let's parse it on demand
   205             # big index, let's parse it on demand
   201             parser = lazyparser(i, self)
   206             parser = lazyparser(i, self)
   202             self.index = lazyindex(parser)
   207             self.index = lazyindex(parser)
   206             l = len(i) / s
   211             l = len(i) / s
   207             self.index = [None] * l
   212             self.index = [None] * l
   208             m = [None] * l
   213             m = [None] * l
   209 
   214 
   210             n = 0
   215             n = 0
   211             for f in xrange(0, len(i), s):
   216             for f in xrange(0, l * s, s):
   212                 # offset, size, base, linkrev, p1, p2, nodeid
   217                 # offset, size, base, linkrev, p1, p2, nodeid
   213                 e = struct.unpack(indexformat, i[f:f + s])
   218                 e = struct.unpack(indexformat, i[f:f + s])
   214                 m[n] = (e[6], n)
   219                 m[n] = (e[6], n)
   215                 self.index[n] = e
   220                 self.index[n] = e
   216                 n += 1
   221                 n += 1
   471 
   476 
   472     def patches(self, t, pl):
   477     def patches(self, t, pl):
   473         """apply a list of patches to a string"""
   478         """apply a list of patches to a string"""
   474         return mdiff.patches(t, pl)
   479         return mdiff.patches(t, pl)
   475 
   480 
       
   481     def chunk(self, rev):
       
   482         start, length = self.start(rev), self.length(rev)
       
   483         end = start + length
       
   484 
       
   485         def loadcache():
       
   486             cache_length = max(4096 * 1024, length) # 4Mo
       
   487             df = self.opener(self.datafile)
       
   488             df.seek(start)
       
   489             self.chunkcache = (start, df.read(cache_length))
       
   490 
       
   491         if not self.chunkcache:
       
   492             loadcache()
       
   493 
       
   494         cache_start = self.chunkcache[0]
       
   495         cache_end = cache_start + len(self.chunkcache[1])
       
   496         if start >= cache_start and end <= cache_end:
       
   497             # it is cached
       
   498             offset = start - cache_start
       
   499         else:
       
   500             loadcache()
       
   501             offset = 0
       
   502 
       
   503         #def checkchunk():
       
   504         #    df = self.opener(self.datafile)
       
   505         #    df.seek(start)
       
   506         #    return df.read(length)
       
   507         #assert s == checkchunk()
       
   508         return decompress(self.chunkcache[1][offset:offset + length])
       
   509 
   476     def delta(self, node):
   510     def delta(self, node):
   477         """return or calculate a delta between a node and its predecessor"""
   511         """return or calculate a delta between a node and its predecessor"""
   478         r = self.rev(node)
   512         r = self.rev(node)
   479         b = self.base(r)
   513         b = self.base(r)
   480         if r == b:
   514         if r == b:
   481             return self.diff(self.revision(self.node(r - 1)),
   515             return self.diff(self.revision(self.node(r - 1)),
   482                              self.revision(node))
   516                              self.revision(node))
   483         else:
   517         else:
   484             f = self.opener(self.datafile)
   518             return self.chunk(r)
   485             f.seek(self.start(r))
       
   486             data = f.read(self.length(r))
       
   487         return decompress(data)
       
   488 
   519 
   489     def revision(self, node):
   520     def revision(self, node):
   490         """return an uncompressed revision of a given"""
   521         """return an uncompressed revision of a given"""
   491         if node == nullid: return ""
   522         if node == nullid: return ""
   492         if self.cache and self.cache[0] == node: return self.cache[2]
   523         if self.cache and self.cache[0] == node: return self.cache[2]
   493 
   524 
   494         # look up what we need to read
   525         # look up what we need to read
   495         text = None
   526         text = None
   496         rev = self.rev(node)
   527         rev = self.rev(node)
   497         start, length, base, link, p1, p2, node = self.index[rev]
   528         base = self.base(rev)
   498         end = start + length
       
   499         if base != rev: start = self.start(base)
       
   500 
   529 
   501         # do we have useful data cached?
   530         # do we have useful data cached?
   502         if self.cache and self.cache[1] >= base and self.cache[1] < rev:
   531         if self.cache and self.cache[1] >= base and self.cache[1] < rev:
   503             base = self.cache[1]
   532             base = self.cache[1]
   504             start = self.start(base + 1)
       
   505             text = self.cache[2]
   533             text = self.cache[2]
   506             last = 0
   534         else:
   507 
   535             text = self.chunk(base)
   508         f = self.opener(self.datafile)
       
   509         f.seek(start)
       
   510         data = f.read(end - start)
       
   511 
       
   512         if text is None:
       
   513             last = self.length(base)
       
   514             text = decompress(data[:last])
       
   515 
   536 
   516         bins = []
   537         bins = []
   517         for r in xrange(base + 1, rev + 1):
   538         for r in xrange(base + 1, rev + 1):
   518             s = self.length(r)
   539             bins.append(self.chunk(r))
   519             bins.append(decompress(data[last:last + s]))
       
   520             last = last + s
       
   521 
   540 
   522         text = mdiff.patches(text, bins)
   541         text = mdiff.patches(text, bins)
   523 
   542 
       
   543         p1, p2 = self.parents(node)
   524         if node != hash(text, p1, p2):
   544         if node != hash(text, p1, p2):
   525             raise RevlogError(_("integrity check failed on %s:%d")
   545             raise RevlogError(_("integrity check failed on %s:%d")
   526                           % (self.datafile, rev))
   546                           % (self.datafile, rev))
   527 
   547 
   528         self.cache = (node, rev, text)
   548         self.cache = (node, rev, text)
   648                 gy = y.next()
   668                 gy = y.next()
   649             else:
   669             else:
   650                 #print "next x"
   670                 #print "next x"
   651                 gx = x.next()
   671                 gx = x.next()
   652 
   672 
   653     def group(self, nodelist, lookup, infocollect = None):
   673     def group(self, nodelist, lookup, infocollect=None):
   654         """calculate a delta group
   674         """calculate a delta group
   655 
   675 
   656         Given a list of changeset revs, return a set of deltas and
   676         Given a list of changeset revs, return a set of deltas and
   657         metadata corresponding to nodes. the first delta is
   677         metadata corresponding to nodes. the first delta is
   658         parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
   678         parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
   659         have this parent as it has all history before these
   679         have this parent as it has all history before these
   660         changesets. parent is parent[0]
   680         changesets. parent is parent[0]
   661         """
   681         """
   662         revs = [self.rev(n) for n in nodelist]
   682         revs = [self.rev(n) for n in nodelist]
   663         needed = dict.fromkeys(revs, 1)
       
   664 
   683 
   665         # if we don't have any revisions touched by these changesets, bail
   684         # if we don't have any revisions touched by these changesets, bail
   666         if not revs:
   685         if not revs:
   667             yield struct.pack(">l", 0)
   686             yield struct.pack(">l", 0)
   668             return
   687             return
   669 
   688 
   670         # add the parent of the first rev
   689         # add the parent of the first rev
   671         p = self.parents(self.node(revs[0]))[0]
   690         p = self.parents(self.node(revs[0]))[0]
   672         revs.insert(0, self.rev(p))
   691         revs.insert(0, self.rev(p))
   673 
   692 
   674         # for each delta that isn't contiguous in the log, we need to
       
   675         # reconstruct the base, reconstruct the result, and then
       
   676         # calculate the delta. We also need to do this where we've
       
   677         # stored a full version and not a delta
       
   678         for i in xrange(0, len(revs) - 1):
       
   679             a, b = revs[i], revs[i + 1]
       
   680             if a + 1 != b or self.base(b) == b:
       
   681                 for j in xrange(self.base(a), a + 1):
       
   682                     needed[j] = 1
       
   683                 for j in xrange(self.base(b), b + 1):
       
   684                     needed[j] = 1
       
   685 
       
   686         # calculate spans to retrieve from datafile
       
   687         needed = needed.keys()
       
   688         needed.sort()
       
   689         spans = []
       
   690         oo = -1
       
   691         ol = 0
       
   692         for n in needed:
       
   693             if n < 0: continue
       
   694             o = self.start(n)
       
   695             l = self.length(n)
       
   696             if oo + ol == o: # can we merge with the previous?
       
   697                 nl = spans[-1][2]
       
   698                 nl.append((n, l))
       
   699                 ol += l
       
   700                 spans[-1] = (oo, ol, nl)
       
   701             else:
       
   702                 oo = o
       
   703                 ol = l
       
   704                 spans.append((oo, ol, [(n, l)]))
       
   705 
       
   706         # read spans in, divide up chunks
       
   707         chunks = {}
       
   708         for span in spans:
       
   709             # we reopen the file for each span to make http happy for now
       
   710             f = self.opener(self.datafile)
       
   711             f.seek(span[0])
       
   712             data = f.read(span[1])
       
   713 
       
   714             # divide up the span
       
   715             pos = 0
       
   716             for r, l in span[2]:
       
   717                 chunks[r] = decompress(data[pos: pos + l])
       
   718                 pos += l
       
   719 
       
   720         # helper to reconstruct intermediate versions
   693         # helper to reconstruct intermediate versions
   721         def construct(text, base, rev):
   694         def construct(text, base, rev):
   722             bins = [chunks[r] for r in xrange(base + 1, rev + 1)]
   695             bins = [self.chunk(r) for r in xrange(base + 1, rev + 1)]
   723             return mdiff.patches(text, bins)
   696             return mdiff.patches(text, bins)
   724 
   697 
   725         # build deltas
   698         # build deltas
   726         deltas = []
       
   727         for d in xrange(0, len(revs) - 1):
   699         for d in xrange(0, len(revs) - 1):
   728             a, b = revs[d], revs[d + 1]
   700             a, b = revs[d], revs[d + 1]
   729             n = self.node(b)
   701             na = self.node(a)
       
   702             nb = self.node(b)
   730 
   703 
   731             if infocollect is not None:
   704             if infocollect is not None:
   732                 infocollect(n)
   705                 infocollect(nb)
   733 
   706 
   734             # do we need to construct a new delta?
   707             # do we need to construct a new delta?
   735             if a + 1 != b or self.base(b) == b:
   708             if a + 1 != b or self.base(b) == b:
   736                 if a >= 0:
   709                 ta = self.revision(na)
   737                     base = self.base(a)
   710                 tb = self.revision(nb)
   738                     ta = chunks[self.base(a)]
       
   739                     ta = construct(ta, base, a)
       
   740                 else:
       
   741                     ta = ""
       
   742 
       
   743                 base = self.base(b)
       
   744                 if a > base:
       
   745                     base = a
       
   746                     tb = ta
       
   747                 else:
       
   748                     tb = chunks[self.base(b)]
       
   749                 tb = construct(tb, base, b)
       
   750                 d = self.diff(ta, tb)
   711                 d = self.diff(ta, tb)
   751             else:
   712             else:
   752                 d = chunks[b]
   713                 d = self.chunk(b)
   753 
   714 
   754             p = self.parents(n)
   715             p = self.parents(nb)
   755             meta = n + p[0] + p[1] + lookup(n)
   716             meta = nb + p[0] + p[1] + lookup(nb)
   756             l = struct.pack(">l", len(meta) + len(d) + 4)
   717             l = struct.pack(">l", len(meta) + len(d) + 4)
   757             yield l
   718             yield l
   758             yield meta
   719             yield meta
   759             yield d
   720             yield d
   760 
   721 
   878 
   839 
   879     def checksize(self):
   840     def checksize(self):
   880         expected = 0
   841         expected = 0
   881         if self.count():
   842         if self.count():
   882             expected = self.end(self.count() - 1)
   843             expected = self.end(self.count() - 1)
       
   844 
   883         try:
   845         try:
   884             f = self.opener(self.datafile)
   846             f = self.opener(self.datafile)
   885             f.seek(0, 2)
   847             f.seek(0, 2)
   886             actual = f.tell()
   848             actual = f.tell()
   887             return expected - actual
   849             dd = actual - expected
   888         except IOError, inst:
   850         except IOError, inst:
   889             if inst.errno == errno.ENOENT:
   851             if inst.errno != errno.ENOENT:
   890                 return 0
   852                 raise
   891             raise
   853             dd = 0
   892 
   854 
   893 
   855         try:
       
   856             f = self.opener(self.indexfile)
       
   857             f.seek(0, 2)
       
   858             actual = f.tell()
       
   859             s = struct.calcsize(indexformat)
       
   860             i = actual / s
       
   861             di = actual - (i * s)
       
   862         except IOError, inst:
       
   863             if inst.errno != errno.ENOENT:
       
   864                 raise
       
   865             di = 0
       
   866 
       
   867         return (dd, di)
       
   868 
       
   869