mercurial/changegroup.py
branchstable
changeset 14687 15200b46165b
parent 14494 1ffeeb91c55d
child 16557 9dba55369cd8
equal deleted inserted replaced
14508:07722bb8a08c 14687:15200b46165b
     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 from i18n import _
     8 from i18n import _
     9 import util
     9 from node import nullrev
       
    10 import mdiff, util
    10 import struct, os, bz2, zlib, tempfile
    11 import struct, os, bz2, zlib, tempfile
       
    12 
       
    13 _BUNDLE10_DELTA_HEADER = "20s20s20s20s"
    11 
    14 
    12 def readexactly(stream, n):
    15 def readexactly(stream, n):
    13     '''read n bytes from stream.read and abort if less was available'''
    16     '''read n bytes from stream.read and abort if less was available'''
    14     s = stream.read(n)
    17     s = stream.read(n)
    15     if len(s) < n:
    18     if len(s) < n:
    41         return x
    44         return x
    42     def flush(self):
    45     def flush(self):
    43         return ""
    46         return ""
    44 
    47 
    45 bundletypes = {
    48 bundletypes = {
    46     "": ("", nocompress),
    49     "": ("", nocompress), # only when using unbundle on ssh and old http servers
       
    50                           # since the unification ssh accepts a header but there
       
    51                           # is no capability signaling it.
    47     "HG10UN": ("HG10UN", nocompress),
    52     "HG10UN": ("HG10UN", nocompress),
    48     "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
    53     "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
    49     "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
    54     "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
    50 }
    55 }
    51 
       
    52 def collector(cl, mmfs, files):
       
    53     # Gather information about changeset nodes going out in a bundle.
       
    54     # We want to gather manifests needed and filelogs affected.
       
    55     def collect(node):
       
    56         c = cl.read(node)
       
    57         files.update(c[3])
       
    58         mmfs.setdefault(c[0], node)
       
    59     return collect
       
    60 
    56 
    61 # hgweb uses this list to communicate its preferred type
    57 # hgweb uses this list to communicate its preferred type
    62 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
    58 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
    63 
    59 
    64 def writebundle(cg, filename, bundletype):
    60 def writebundle(cg, filename, bundletype):
    93         empty = False
    89         empty = False
    94         count = 0
    90         count = 0
    95         while not empty or count <= 2:
    91         while not empty or count <= 2:
    96             empty = True
    92             empty = True
    97             count += 1
    93             count += 1
    98             while 1:
    94             while True:
    99                 chunk = getchunk(cg)
    95                 chunk = getchunk(cg)
   100                 if not chunk:
    96                 if not chunk:
   101                     break
    97                     break
   102                 empty = False
    98                 empty = False
   103                 fh.write(z.compress(chunkheader(len(chunk))))
    99                 fh.write(z.compress(chunkheader(len(chunk))))
   133     else:
   129     else:
   134         raise util.Abort("unknown bundle compression '%s'" % alg)
   130         raise util.Abort("unknown bundle compression '%s'" % alg)
   135     return util.chunkbuffer(generator(fh))
   131     return util.chunkbuffer(generator(fh))
   136 
   132 
   137 class unbundle10(object):
   133 class unbundle10(object):
       
   134     deltaheader = _BUNDLE10_DELTA_HEADER
       
   135     deltaheadersize = struct.calcsize(deltaheader)
   138     def __init__(self, fh, alg):
   136     def __init__(self, fh, alg):
   139         self._stream = decompressor(fh, alg)
   137         self._stream = decompressor(fh, alg)
   140         self._type = alg
   138         self._type = alg
   141         self.callback = None
   139         self.callback = None
   142     def compressed(self):
   140     def compressed(self):
   159             return 0
   157             return 0
   160         if self.callback:
   158         if self.callback:
   161             self.callback()
   159             self.callback()
   162         return l - 4
   160         return l - 4
   163 
   161 
   164     def chunk(self):
   162     def changelogheader(self):
   165         """return the next chunk from changegroup 'source' as a string"""
   163         """v10 does not have a changelog header chunk"""
   166         l = self.chunklength()
   164         return {}
   167         return readexactly(self._stream, l)
   165 
   168 
   166     def manifestheader(self):
   169     def parsechunk(self):
   167         """v10 does not have a manifest header chunk"""
       
   168         return {}
       
   169 
       
   170     def filelogheader(self):
       
   171         """return the header of the filelogs chunk, v10 only has the filename"""
   170         l = self.chunklength()
   172         l = self.chunklength()
   171         if not l:
   173         if not l:
   172             return {}
   174             return {}
   173         h = readexactly(self._stream, 80)
   175         fname = readexactly(self._stream, l)
   174         node, p1, p2, cs = struct.unpack("20s20s20s20s", h)
   176         return dict(filename=fname)
   175         data = readexactly(self._stream, l - 80)
   177 
   176         return dict(node=node, p1=p1, p2=p2, cs=cs, data=data)
   178     def _deltaheader(self, headertuple, prevnode):
       
   179         node, p1, p2, cs = headertuple
       
   180         if prevnode is None:
       
   181             deltabase = p1
       
   182         else:
       
   183             deltabase = prevnode
       
   184         return node, p1, p2, deltabase, cs
       
   185 
       
   186     def deltachunk(self, prevnode):
       
   187         l = self.chunklength()
       
   188         if not l:
       
   189             return {}
       
   190         headerdata = readexactly(self._stream, self.deltaheadersize)
       
   191         header = struct.unpack(self.deltaheader, headerdata)
       
   192         delta = readexactly(self._stream, l - self.deltaheadersize)
       
   193         node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode)
       
   194         return dict(node=node, p1=p1, p2=p2, cs=cs,
       
   195                     deltabase=deltabase, delta=delta)
   177 
   196 
   178 class headerlessfixup(object):
   197 class headerlessfixup(object):
   179     def __init__(self, fh, h):
   198     def __init__(self, fh, h):
   180         self._h = h
   199         self._h = h
   181         self._fh = fh
   200         self._fh = fh
   201     if magic != 'HG':
   220     if magic != 'HG':
   202         raise util.Abort(_('%s: not a Mercurial bundle') % fname)
   221         raise util.Abort(_('%s: not a Mercurial bundle') % fname)
   203     if version != '10':
   222     if version != '10':
   204         raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
   223         raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
   205     return unbundle10(fh, alg)
   224     return unbundle10(fh, alg)
       
   225 
       
   226 class bundle10(object):
       
   227     deltaheader = _BUNDLE10_DELTA_HEADER
       
   228     def __init__(self, lookup):
       
   229         self._lookup = lookup
       
   230     def close(self):
       
   231         return closechunk()
       
   232     def fileheader(self, fname):
       
   233         return chunkheader(len(fname)) + fname
       
   234     def revchunk(self, revlog, rev, prev):
       
   235         node = revlog.node(rev)
       
   236         p1, p2 = revlog.parentrevs(rev)
       
   237         base = prev
       
   238 
       
   239         prefix = ''
       
   240         if base == nullrev:
       
   241             delta = revlog.revision(node)
       
   242             prefix = mdiff.trivialdiffheader(len(delta))
       
   243         else:
       
   244             delta = revlog.revdiff(base, rev)
       
   245         linknode = self._lookup(revlog, node)
       
   246         p1n, p2n = revlog.parents(node)
       
   247         basenode = revlog.node(base)
       
   248         meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode)
       
   249         meta += prefix
       
   250         l = len(meta) + len(delta)
       
   251         yield chunkheader(l)
       
   252         yield meta
       
   253         yield delta
       
   254     def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
       
   255         # do nothing with basenode, it is implicitly the previous one in HG10
       
   256         return struct.pack(self.deltaheader, node, p1n, p2n, linknode)