MQ: tidy up if a qpush is interrupted.
This aborts the transaction, makes sure the dirtied dirstate isn't
written out, and cleans up the working directory.
"""
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 GNU General Public License, incorporated herein by reference.
"""
from i18n import gettext as _
from demandload import *
demandload(globals(), "struct os bz2 zlib util tempfile")
def getchunk(source):
"""get a chunk from a changegroup"""
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
def chunkiter(source):
"""iterate through the chunks in source"""
while 1:
c = getchunk(source)
if not c:
break
yield c
def genchunk(data):
"""build a changegroup chunk"""
header = struct.pack(">l", len(data)+ 4)
return "%s%s" % (header, data)
def closechunk():
return struct.pack(">l", 0)
class nocompress(object):
def compress(self, x):
return x
def flush(self):
return ""
bundletypes = {
"": ("", nocompress),
"HG10UN": ("HG10UN", nocompress),
"HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
"HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
}
def writebundle(cg, filename, bundletype):
"""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:
if os.path.exists(filename):
raise util.Abort(_("file '%s' already exists") % filename)
fh = open(filename, "wb")
else:
fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
fh = os.fdopen(fd, "wb")
cleanup = filename
header, compressor = bundletypes[bundletype]
fh.write(header)
z = compressor()
# 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
empty = False
while not empty:
empty = True
for chunk in chunkiter(cg):
empty = False
fh.write(z.compress(genchunk(chunk)))
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)
def readbundle(fh, fname):
header = fh.read(6)
if not header.startswith("HG"):
raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
elif not header.startswith("HG10"):
raise util.Abort(_("%s: unknown bundle version") % fname)
if header == "HG10BZ":
def generator(f):
zd = bz2.BZ2Decompressor()
zd.decompress("BZ")
for chunk in util.filechunkiter(f, 4096):
yield zd.decompress(chunk)
return util.chunkbuffer(generator(fh))
elif header == "HG10UN":
return fh
raise util.Abort(_("%s: unknown bundle compression type")
% fname)