##// END OF EJS Templates
clone: only use stream when we understand the revlog format...
clone: only use stream when we understand the revlog format This patch fixes issues with stream cloning in the presense of parentdelta, lwcopy and similar additions that change the interpretation of the revlog format, or the format itself. Currently, the stream capability is sent like this: stream=<version of changelog> But the client doesn't actually check the version number; also, it only checks the changelog and it doesn't capture the interpretation-changes and flag-changes in parentdelta and lwcopy. This patch removes the 'stream' capability whenever we use a non-basic revlog format, to prevent old clients from receiving incorrect data. In those cases, a new capability called 'streamreqs' is added instead. Instead of a revlog version, it comes with a list of revlog-format relevant requirements, which are a subset of the repository requirements, excluding things that are not relevant for stream. New clients use this to determine whether or not they can stream. Old clients only look for the 'stream' capability, as always. New servers will still send this when serving old repositories.

File last commit:

r12044:bcc71395 default
r12296:d7fff529 default
Show More
changegroup.py
170 lines | 5.1 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # changegroup.py - Mercurial changegroup manipulation functions
#
# Copyright 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
Replace demandload with new demandimport
r3877
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Simon Heimberg
separate import lines from mercurial and general python modules
r8312 import util
import struct, os, bz2, zlib, tempfile
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981
def getchunk(source):
Greg Ward
Improve some docstrings relating to changegroups and prepush().
r9437 """return the next chunk from changegroup 'source' as a string"""
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981 d = source.read(4)
if not d:
return ""
l = struct.unpack(">l", d)[0]
if l <= 4:
return ""
d = source.read(l - 4)
if len(d) < l - 4:
raise util.Abort(_("premature EOF reading chunk"
" (got %d bytes, expected %d)")
% (len(d), l - 4))
return d
Augie Fackler
localrepo: show indeterminate progress for incoming data...
r10430 def chunkiter(source, progress=None):
Greg Ward
Improve some docstrings relating to changegroups and prepush().
r9437 """iterate through the chunks in source, yielding a sequence of chunks
(strings)"""
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981 while 1:
c = getchunk(source)
if not c:
break
Augie Fackler
localrepo: show indeterminate progress for incoming data...
r10430 elif progress is not None:
progress()
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981 yield c
Matt Mackall
changegroup: avoid large copies...
r5368 def chunkheader(length):
Greg Ward
Improve some docstrings relating to changegroups and prepush().
r9437 """return a changegroup chunk header (string)"""
Matt Mackall
changegroup: avoid large copies...
r5368 return struct.pack(">l", length + 4)
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981
def closechunk():
Greg Ward
Improve some docstrings relating to changegroups and prepush().
r9437 """return a changegroup chunk header (string) for a zero-length chunk"""
Thomas Arendsen Hein
make incoming work via ssh (issue139); move chunk code into separate module....
r1981 return struct.pack(">l", 0)
Matt Mackall
move write_bundle to changegroup.py
r3659 class nocompress(object):
def compress(self, x):
return x
def flush(self):
return ""
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 bundletypes = {
Benoit Boissinot
fix writebundle for bz2 bundles
r3704 "": ("", nocompress),
"HG10UN": ("HG10UN", nocompress),
Alexis S. L. Carvalho
changegroup.py: delay the loading of the bz2 and zlib modules
r3762 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
"HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 }
Dirkjan Ochtman
localrepo: unify changegroup and changegroupsubset code paths a bit
r10356 def collector(cl, mmfs, files):
# Gather information about changeset nodes going out in a bundle.
# We want to gather manifests needed and filelogs affected.
def collect(node):
c = cl.read(node)
Benoit Boissinot
changegroup*(): use set instead of dict
r11648 files.update(c[3])
Dirkjan Ochtman
localrepo: unify changegroup and changegroupsubset code paths a bit
r10356 mmfs.setdefault(c[0], node)
return collect
Martin Geisler
typos: "it's" -> "its"
r9087 # hgweb uses this list to communicate its preferred type
Dirkjan Ochtman
hgweb: use bundletypes from mercurial.changegroup
r6152 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
Thomas Arendsen Hein
Use 'bundletype' instead of 'type' to not shadow built-in function.
r3706 def writebundle(cg, filename, bundletype):
Matt Mackall
move write_bundle to changegroup.py
r3659 """Write a bundle file and return its filename.
Existing files will not be overwritten.
If no filename is specified, a temporary file is created.
bz2 compression can be turned off.
The bundle file will be deleted in case of errors.
"""
fh = None
cleanup = None
try:
if filename:
fh = open(filename, "wb")
else:
fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
fh = os.fdopen(fd, "wb")
cleanup = filename
Thomas Arendsen Hein
Use 'bundletype' instead of 'type' to not shadow built-in function.
r3706 header, compressor = bundletypes[bundletype]
Benoit Boissinot
fix writebundle for bz2 bundles
r3704 fh.write(header)
z = compressor()
Matt Mackall
unduplicate bundle writing code from httprepo
r3662
Matt Mackall
move write_bundle to changegroup.py
r3659 # parse the changegroup data, otherwise we will block
# in case of sshrepo because we don't know the end of the stream
# an empty chunkiter is the end of the changegroup
Alexis S. L. Carvalho
allow the creation of bundles with empty changelog/manifest chunks
r5906 # a changegroup has at least 2 chunkiters (changelog and manifest).
# after that, an empty chunkiter is the end of the changegroup
Matt Mackall
move write_bundle to changegroup.py
r3659 empty = False
Alexis S. L. Carvalho
allow the creation of bundles with empty changelog/manifest chunks
r5906 count = 0
while not empty or count <= 2:
Matt Mackall
move write_bundle to changegroup.py
r3659 empty = True
Alexis S. L. Carvalho
allow the creation of bundles with empty changelog/manifest chunks
r5906 count += 1
Matt Mackall
move write_bundle to changegroup.py
r3659 for chunk in chunkiter(cg):
empty = False
Matt Mackall
changegroup: avoid large copies...
r5368 fh.write(z.compress(chunkheader(len(chunk))))
pos = 0
while pos < len(chunk):
next = pos + 2**20
fh.write(z.compress(chunk[pos:next]))
pos = next
Matt Mackall
move write_bundle to changegroup.py
r3659 fh.write(z.compress(closechunk()))
fh.write(z.flush())
cleanup = None
return filename
finally:
if fh is not None:
fh.close()
if cleanup is not None:
os.unlink(cleanup)
Matt Mackall
create a readbundle function
r3660
Matt Mackall
bundle: factor out decompressor
r12041 def decompressor(fh, alg):
if alg == 'UN':
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 return fh
Matt Mackall
bundle: factor out decompressor
r12041 elif alg == 'GZ':
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 def generator(f):
zd = zlib.decompressobj()
for chunk in f:
yield zd.decompress(chunk)
Matt Mackall
bundle: factor out decompressor
r12041 elif alg == 'BZ':
Matt Mackall
create a readbundle function
r3660 def generator(f):
zd = bz2.BZ2Decompressor()
zd.decompress("BZ")
for chunk in util.filechunkiter(f, 4096):
yield zd.decompress(chunk)
Matt Mackall
bundle: factor out decompressor
r12041 else:
raise util.Abort("unknown bundle compression '%s'" % alg)
return generator(fh)
Matt Mackall
bundle: introduce bundle class
r12043 class unbundle10(object):
def __init__(self, fh, alg):
self._stream = util.chunkbuffer(decompressor(fh, alg))
Matt Mackall
bundlerepo: remove duplication of bundle decompressors
r12044 self._type = alg
def compressed(self):
return self._type != 'UN'
Matt Mackall
bundle: introduce bundle class
r12043 def read(self, l):
return self._stream.read(l)
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 def readbundle(fh, fname):
header = fh.read(6)
Matt Mackall
bundle: unify/refactor unbundle/readbundle
r12042
if not fname:
fname = "stream"
if not header.startswith('HG') and header.startswith('\0'):
# headerless bundle, clean things up
def fixup(f, h):
yield h
for x in f:
yield x
fh = fixup(fh, header)
header = "HG10UN"
magic, version, alg = header[0:2], header[2:4], header[4:6]
if magic != 'HG':
raise util.Abort(_('%s: not a Mercurial bundle') % fname)
if version != '10':
raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
Matt Mackall
bundle: introduce bundle class
r12043 return unbundle10(fh, alg)