mercurial/revlog.py
changeset 4980 fc44c8df9d99
parent 4979 06abdaf78788
child 4981 e7131935fbb3
equal deleted inserted replaced
4979:06abdaf78788 4980:fc44c8df9d99
    40     s.update(text)
    40     s.update(text)
    41     return s.digest()
    41     return s.digest()
    42 
    42 
    43 def compress(text):
    43 def compress(text):
    44     """ generate a possibly-compressed representation of text """
    44     """ generate a possibly-compressed representation of text """
    45     if not text: return ("", text)
    45     if not text:
       
    46         return ("", text)
    46     if len(text) < 44:
    47     if len(text) < 44:
    47         if text[0] == '\0': return ("", text)
    48         if text[0] == '\0':
       
    49             return ("", text)
    48         return ('u', text)
    50         return ('u', text)
    49     bin = zlib.compress(text)
    51     bin = zlib.compress(text)
    50     if len(bin) > len(text):
    52     if len(bin) > len(text):
    51         if text[0] == '\0': return ("", text)
    53         if text[0] == '\0':
       
    54             return ("", text)
    52         return ('u', text)
    55         return ('u', text)
    53     return ("", bin)
    56     return ("", bin)
    54 
    57 
    55 def decompress(bin):
    58 def decompress(bin):
    56     """ decompress the given input """
    59     """ decompress the given input """
    57     if not bin: return bin
    60     if not bin:
       
    61         return bin
    58     t = bin[0]
    62     t = bin[0]
    59     if t == '\0': return bin
    63     if t == '\0':
    60     if t == 'x': return zlib.decompress(bin)
    64         return bin
    61     if t == 'u': return bin[1:]
    65     if t == 'x':
       
    66         return zlib.decompress(bin)
       
    67     if t == 'u':
       
    68         return bin[1:]
    62     raise RevlogError(_("unknown compression type %r") % t)
    69     raise RevlogError(_("unknown compression type %r") % t)
    63 
    70 
    64 indexformatv0 = ">4l20s20s20s"
    71 indexformatv0 = ">4l20s20s20s"
    65 v0shaoffset = 56
    72 v0shaoffset = 56
    66 # index ng:
    73 # index ng:
   104         during a commit, we need to make sure the rev being added is
   111         during a commit, we need to make sure the rev being added is
   105         not a duplicate.  This requires loading the entire index,
   112         not a duplicate.  This requires loading the entire index,
   106         which is fairly slow.  loadmap can load up just the node map,
   113         which is fairly slow.  loadmap can load up just the node map,
   107         which takes much less time.
   114         which takes much less time.
   108         """
   115         """
   109         if self.allmap: return
   116         if self.allmap:
       
   117             return
   110         end = self.datasize
   118         end = self.datasize
   111         self.allmap = 1
   119         self.allmap = 1
   112         cur = 0
   120         cur = 0
   113         count = 0
   121         count = 0
   114         blocksize = self.s * 256
   122         blocksize = self.s * 256
   124                     break
   132                     break
   125                 off += self.s
   133                 off += self.s
   126             cur += blocksize
   134             cur += blocksize
   127 
   135 
   128     def loadblock(self, blockstart, blocksize, data=None):
   136     def loadblock(self, blockstart, blocksize, data=None):
   129         if self.all: return
   137         if self.all:
       
   138             return
   130         if data is None:
   139         if data is None:
   131             self.dataf.seek(blockstart)
   140             self.dataf.seek(blockstart)
   132             if blockstart + blocksize > self.datasize:
   141             if blockstart + blocksize > self.datasize:
   133                 # the revlog may have grown since we've started running,
   142                 # the revlog may have grown since we've started running,
   134                 # but we don't have space in self.index for more entries.
   143                 # but we don't have space in self.index for more entries.
   149                 self.map[n] = i + x
   158                 self.map[n] = i + x
   150             off += self.s
   159             off += self.s
   151 
   160 
   152     def findnode(self, node):
   161     def findnode(self, node):
   153         """search backwards through the index file for a specific node"""
   162         """search backwards through the index file for a specific node"""
   154         if self.allmap: return None
   163         if self.allmap:
       
   164             return None
   155 
   165 
   156         # hg log will cause many many searches for the manifest
   166         # hg log will cause many many searches for the manifest
   157         # nodes.  After we get called a few times, just load the whole
   167         # nodes.  After we get called a few times, just load the whole
   158         # thing.
   168         # thing.
   159         if self.mapfind_count > 8:
   169         if self.mapfind_count > 8:
   192                     break
   202                     break
   193             end -= blocksize
   203             end -= blocksize
   194         return None
   204         return None
   195 
   205 
   196     def loadindex(self, i=None, end=None):
   206     def loadindex(self, i=None, end=None):
   197         if self.all: return
   207         if self.all:
       
   208             return
   198         all = False
   209         all = False
   199         if i == None:
   210         if i == None:
   200             blockstart = 0
   211             blockstart = 0
   201             blocksize = (512 / self.s) * self.s
   212             blocksize = (512 / self.s) * self.s
   202             end = self.datasize
   213             end = self.datasize
   211                 blocksize = self.s * 64
   222                 blocksize = self.s * 64
   212                 end = blockstart + blocksize
   223                 end = blockstart + blocksize
   213         while blockstart < end:
   224         while blockstart < end:
   214             self.loadblock(blockstart, blocksize)
   225             self.loadblock(blockstart, blocksize)
   215             blockstart += blocksize
   226             blockstart += blocksize
   216         if all: self.all = True
   227         if all:
       
   228             self.all = True
   217 
   229 
   218 class lazyindex(object):
   230 class lazyindex(object):
   219     """a lazy version of the index array"""
   231     """a lazy version of the index array"""
   220     def __init__(self, parser):
   232     def __init__(self, parser):
   221         self.p = parser
   233         self.p = parser
   275     def __setitem__(self, key, val):
   287     def __setitem__(self, key, val):
   276         self.p.map[key] = val
   288         self.p.map[key] = val
   277     def __delitem__(self, key):
   289     def __delitem__(self, key):
   278         del self.p.map[key]
   290         del self.p.map[key]
   279 
   291 
   280 class RevlogError(Exception): pass
   292 class RevlogError(Exception):
   281 class LookupError(RevlogError): pass
   293     pass
       
   294 class LookupError(RevlogError):
       
   295     pass
   282 
   296 
   283 def getoffset(q):
   297 def getoffset(q):
   284     if q & 0xFFFF:
   298     if q & 0xFFFF:
   285         raise RevlogError(_('incompatible revision flag %x') % q)
   299         raise RevlogError(_('incompatible revision flag %x') % q)
   286     return int(q >> 16)
   300     return int(q >> 16)
   472         """loads the map from the lazy parser"""
   486         """loads the map from the lazy parser"""
   473         if isinstance(self.nodemap, lazymap):
   487         if isinstance(self.nodemap, lazymap):
   474             self.nodemap.p.loadmap()
   488             self.nodemap.p.loadmap()
   475             self.nodemap = self.nodemap.p.map
   489             self.nodemap = self.nodemap.p.map
   476 
   490 
   477     def _inline(self): return self.version & REVLOGNGINLINEDATA
   491     def _inline(self):
   478 
   492         return self.version & REVLOGNGINLINEDATA
   479     def tip(self): return self.node(len(self.index) - 2)
   493     def tip(self):
   480     def count(self): return len(self.index) - 1
   494         return self.node(len(self.index) - 2)
   481     def node(self, rev):
   495     def count(self):
   482         return self.index[rev][7]
   496         return len(self.index) - 1
       
   497 
   483     def rev(self, node):
   498     def rev(self, node):
   484         try:
   499         try:
   485             return self.nodemap[node]
   500             return self.nodemap[node]
   486         except KeyError:
   501         except KeyError:
   487             raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
   502             raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
       
   503     def node(self, rev):
       
   504         return self.index[rev][7]
   488     def linkrev(self, node):
   505     def linkrev(self, node):
   489         return self.index[self.rev(node)][4]
   506         return self.index[self.rev(node)][4]
   490     def parents(self, node):
   507     def parents(self, node):
   491         d = self.index[self.rev(node)][5:7]
   508         d = self.index[self.rev(node)][5:7]
   492         return (self.node(d[0]), self.node(d[1]))
   509         return (self.node(d[0]), self.node(d[1]))
   493     def parentrevs(self, rev):
   510     def parentrevs(self, rev):
   494         return self.index[rev][5:7]
   511         return self.index[rev][5:7]
   495     def start(self, rev):
   512     def start(self, rev):
   496         return getoffset(self.index[rev][0])
   513         return getoffset(self.index[rev][0])
   497     def end(self, rev): return self.start(rev) + self.length(rev)
   514     def end(self, rev):
       
   515         return self.start(rev) + self.length(rev)
       
   516     def length(self, rev):
       
   517         return self.index[rev][1]
       
   518     def base(self, rev):
       
   519         return self.index[rev][3]
   498 
   520 
   499     def size(self, rev):
   521     def size(self, rev):
   500         """return the length of the uncompressed text for a given revision"""
   522         """return the length of the uncompressed text for a given revision"""
   501         l = self.index[rev][2]
   523         l = self.index[rev][2]
   502         if l >= 0:
   524         if l >= 0:
   522         l = len(text)
   544         l = len(text)
   523         for x in xrange(base + 1, rev + 1):
   545         for x in xrange(base + 1, rev + 1):
   524             l = mdiff.patchedsize(l, self.chunk(x))
   546             l = mdiff.patchedsize(l, self.chunk(x))
   525         return l
   547         return l
   526         """
   548         """
   527 
       
   528     def length(self, rev):
       
   529         return self.index[rev][1]
       
   530     def base(self, rev):
       
   531         return self.index[rev][3]
       
   532 
   549 
   533     def reachable(self, node, stop=None):
   550     def reachable(self, node, stop=None):
   534         """return a hash of all nodes ancestral to a given node, including
   551         """return a hash of all nodes ancestral to a given node, including
   535          the node itself, stopping when stop is matched"""
   552          the node itself, stopping when stop is matched"""
   536         reachable = {}
   553         reachable = {}
   762             except LookupError:
   779             except LookupError:
   763                 pass # may be partial hex id
   780                 pass # may be partial hex id
   764         try:
   781         try:
   765             # str(rev)
   782             # str(rev)
   766             rev = int(id)
   783             rev = int(id)
   767             if str(rev) != id: raise ValueError
   784             if str(rev) != id:
   768             if rev < 0: rev = self.count() + rev
   785                 raise ValueError
   769             if rev < 0 or rev >= self.count(): raise ValueError
   786             if rev < 0:
       
   787                 rev = self.count() + rev
       
   788             if rev < 0 or rev >= self.count():
       
   789                 raise ValueError
   770             return self.node(rev)
   790             return self.node(rev)
   771         except (ValueError, OverflowError):
   791         except (ValueError, OverflowError):
   772             pass
   792             pass
   773         if len(id) == 40:
   793         if len(id) == 40:
   774             try:
   794             try:
   798     def lookup(self, id):
   818     def lookup(self, id):
   799         """locate a node based on:
   819         """locate a node based on:
   800             - revision number or str(revision number)
   820             - revision number or str(revision number)
   801             - nodeid or subset of hex nodeid
   821             - nodeid or subset of hex nodeid
   802         """
   822         """
   803 
       
   804         n = self._match(id)
   823         n = self._match(id)
   805         if n is not None:
   824         if n is not None:
   806             return n
   825             return n
   807         n = self._partialmatch(id)
   826         n = self._partialmatch(id)
   808         if n:
   827         if n:
   849             offset = start - cache_start
   868             offset = start - cache_start
   850         else:
   869         else:
   851             loadcache(df)
   870             loadcache(df)
   852             offset = 0
   871             offset = 0
   853 
   872 
   854         #def checkchunk():
       
   855         #    df = self.opener(self.datafile)
       
   856         #    df.seek(start)
       
   857         #    return df.read(length)
       
   858         #assert s == checkchunk()
       
   859         return decompress(self._io.chunkcache[1][offset:offset + length])
   873         return decompress(self._io.chunkcache[1][offset:offset + length])
   860 
   874 
   861     def delta(self, node):
   875     def delta(self, node):
   862         """return or calculate a delta between a node and its predecessor"""
   876         """return or calculate a delta between a node and its predecessor"""
   863         r = self.rev(node)
   877         r = self.rev(node)
   873             return self.diff(self.revision(self.node(rev1)),
   887             return self.diff(self.revision(self.node(rev1)),
   874                              self.revision(self.node(rev2)))
   888                              self.revision(self.node(rev2)))
   875 
   889 
   876     def revision(self, node):
   890     def revision(self, node):
   877         """return an uncompressed revision of a given"""
   891         """return an uncompressed revision of a given"""
   878         if node == nullid: return ""
   892         if node == nullid:
   879         if self.cache and self.cache[0] == node: return self.cache[2]
   893             return ""
       
   894         if self.cache and self.cache[0] == node:
       
   895             return self.cache[2]
   880 
   896 
   881         # look up what we need to read
   897         # look up what we need to read
   882         text = None
   898         text = None
   883         rev = self.rev(node)
   899         rev = self.rev(node)
   884         base = self.base(rev)
   900         base = self.base(rev)
   976             dfh = None
   992             dfh = None
   977         ifh = self.opener(self.indexfile, "a+")
   993         ifh = self.opener(self.indexfile, "a+")
   978         return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
   994         return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
   979 
   995 
   980     def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
   996     def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
   981         if text is None: text = ""
   997         if text is None:
   982         if p1 is None: p1 = self.tip()
   998             text = ""
   983         if p2 is None: p2 = nullid
   999         if p1 is None:
       
  1000             p1 = self.tip()
       
  1001         if p2 is None:
       
  1002             p2 = nullid
   984 
  1003 
   985         node = hash(text, p1, p2)
  1004         node = hash(text, p1, p2)
   986 
  1005 
   987         if node in self.nodemap:
  1006         if node in self.nodemap:
   988             return node
  1007             return node